/*
  * Copyright (c) 2016 MediaTek Inc.  All rights reserved.
  *
  * This software is available to you under a choice of one of two
  * licenses.  You may choose to be licensed under the terms of the GNU
  * General Public License (GPL) Version 2, available from the file
  * COPYING in the main directory of this source tree, or the
  * BSD license below:
  *
  *     Redistribution and use in source and binary forms, with or
  *     without modification, are permitted provided that the following
  *     conditions are met:
  *
  *      - Redistributions of source code must retain the above
  *        copyright notice, this list of conditions and the following
  *        disclaimer.
  *
  *      - Redistributions in binary form must reproduce the above
  *        copyright notice, this list of conditions and the following
  *        disclaimer in the documentation and/or other materials
  *        provided with the distribution.
  *
  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
  * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
  * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
  * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
  * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
  * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
  * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
  * SOFTWARE.
 ****************************************************************************/



#ifdef RTMP_MAC_PCI
#include	"rt_config.h"


static INT desc_ring_alloc(RTMP_ADAPTER *pAd, RTMP_DMABUF *pDescRing, INT size)
{
	VOID *pci_dev =
	    (pAd->infType == RTMP_DEV_INF_RBUS) ? NULL : ((POS_COOKIE) (pAd->OS_Cookie))->pci_dev;

	pDescRing->AllocSize = size;
	RtmpAllocDescBuf(pci_dev,
			 0, pDescRing->AllocSize, FALSE, &pDescRing->AllocVa, &pDescRing->AllocPa);

	if (pDescRing->AllocVa == NULL) {
		DBGPRINT_ERR(("Failed to allocate a big buffer\n"));
		return ERRLOG_OUT_OF_SHARED_MEMORY;
	}

	/* Zero init this memory block */
	NdisZeroMemory(pDescRing->AllocVa, size);

	return 0;
}


static INT desc_ring_free(RTMP_ADAPTER *pAd, RTMP_DMABUF *pDescRing)
{
	VOID *pci_dev =
	    (pAd->infType == RTMP_DEV_INF_RBUS) ? NULL : ((POS_COOKIE) (pAd->OS_Cookie))->pci_dev;

	if (pDescRing->AllocVa) {
		RtmpFreeDescBuf(pci_dev,
				pDescRing->AllocSize, pDescRing->AllocVa, pDescRing->AllocPa);
	}
	NdisZeroMemory(pDescRing, sizeof(RTMP_DMABUF));

	return TRUE;
}


VOID RTMPResetTxRxRingMemory(RTMP_ADAPTER *pAd)
{
	int index, j;
	RTMP_TX_RING *pTxRing;
	TXD_STRUC *pTxD;
	RTMP_DMACB *dma_cb;
#ifdef RT_BIG_ENDIAN
	TXD_STRUC *pDestTxD;
	UCHAR tx_hw_info[TXD_SIZE];
#endif				/* RT_BIG_ENDIAN */
	PNDIS_PACKET pPacket;
	ULONG IrqFlags;


	/* Free TxSwQueue Packet */
	for (index = 0; index < NUM_OF_TX_RING; index++) {
		QUEUE_ENTRY *pEntry;
		QUEUE_HEADER *pQueue;

		RTMP_IRQ_LOCK(&pAd->irq_lock, IrqFlags);
		pQueue = &pAd->TxSwQueue[index];
		while (pQueue->Head) {
			pEntry = RemoveHeadQueue(pQueue);
			pPacket = QUEUE_ENTRY_TO_PACKET(pEntry);
			RELEASE_NDIS_PACKET(pAd, pPacket, NDIS_STATUS_FAILURE);
		}
		RTMP_IRQ_UNLOCK(&pAd->irq_lock, IrqFlags);
	}


	/* Free Tx Ring Packet */
	for (index = 0; index < NUM_OF_TX_RING; index++) {
		pTxRing = &pAd->TxRing[index];
		for (j = 0; j < TX_RING_SIZE; j++) {
			dma_cb = &pTxRing->Cell[j];
#ifdef RT_BIG_ENDIAN
			pDestTxD = (PTXD_STRUC) (dma_cb->AllocVa);
			NdisMoveMemory(&tx_hw_info[0], (UCHAR *) pDestTxD, TXD_SIZE);
			pTxD = (TXD_STRUC *) &tx_hw_info[0];
			RTMPDescriptorEndianChange((PUCHAR) pTxD, TYPE_TXD);
#else
			pTxD = (PTXD_STRUC) (dma_cb->AllocVa);
#endif				/* RT_BIG_ENDIAN */
			pPacket = dma_cb->pNdisPacket;

			if (pPacket) {
				PCI_UNMAP_SINGLE(pAd, pTxD->SDPtr0, pTxD->SDLen0,
						 RTMP_PCI_DMA_TODEVICE);
				RELEASE_NDIS_PACKET(pAd, pPacket, NDIS_STATUS_SUCCESS);
			}
			/*Always assign pNdisPacket as NULL after clear */
			dma_cb->pNdisPacket = NULL;

			pPacket = dma_cb->pNextNdisPacket;
			if (pPacket) {
				PCI_UNMAP_SINGLE(pAd, pTxD->SDPtr1, pTxD->SDLen1,
						 RTMP_PCI_DMA_TODEVICE);
				RELEASE_NDIS_PACKET(pAd, pPacket, NDIS_STATUS_SUCCESS);
			}
			/*Always assign pNextNdisPacket as NULL after clear */
			dma_cb->pNextNdisPacket = NULL;

#ifdef RT_BIG_ENDIAN
			RTMPDescriptorEndianChange((PUCHAR) pTxD, TYPE_TXD);
			WriteBackToDescriptor((PUCHAR) pDestTxD, (PUCHAR) pTxD, FALSE, TYPE_TXD);
#endif				/* RT_BIG_ENDIAN */
		}
	}

	for (index = 0; index < NUM_OF_RX_RING; index++) {
		for (j = RX_RING_SIZE - 1; j >= 0; j--) {
			dma_cb = &pAd->RxRing[index].Cell[j];
			if ((dma_cb->DmaBuf.AllocVa) && (dma_cb->pNdisPacket)) {
				PCI_UNMAP_SINGLE(pAd, dma_cb->DmaBuf.AllocPa,
						 dma_cb->DmaBuf.AllocSize, RTMP_PCI_DMA_FROMDEVICE);
				RELEASE_NDIS_PACKET(pAd, dma_cb->pNdisPacket, NDIS_STATUS_SUCCESS);
			}
		}
		NdisZeroMemory(pAd->RxRing[index].Cell, RX_RING_SIZE * sizeof(RTMP_DMACB));
	}

	if (pAd->FragFrame.pFragPacket) {
		RELEASE_NDIS_PACKET(pAd, pAd->FragFrame.pFragPacket, NDIS_STATUS_SUCCESS);
		pAd->FragFrame.pFragPacket = NULL;
	}

	NdisFreeSpinLock(&pAd->CmdQLock);
}


VOID RTMPFreeTxRxRingMemory(RTMP_ADAPTER *pAd)
{
	INT num;
	VOID *pci_dev =
	    (pAd->infType == RTMP_DEV_INF_RBUS) ? NULL : ((POS_COOKIE) (pAd->OS_Cookie))->pci_dev;

	DBGPRINT(RT_DEBUG_TRACE, ("--> RTMPFreeTxRxRingMemory\n"));


	/* Free Rx/Mgmt Desc buffer */
	for (num = 0; num < NUM_OF_RX_RING; num++)
		desc_ring_free(pAd, &pAd->RxDescRing[num]);

	desc_ring_free(pAd, &pAd->MgmtDescRing);
#ifdef CONFIG_ANDES_SUPPORT
	desc_ring_free(pAd, &pAd->CtrlDescRing);
#endif				/* CONFIG_ANDES_SUPPORT */

	/* Free 1st TxBufSpace and TxDesc buffer */
	for (num = 0; num < NUM_OF_TX_RING; num++) {
		if (pAd->TxBufSpace[num].AllocVa) {
			RTMP_FreeFirstTxBuffer(pci_dev,
					       pAd->TxBufSpace[num].AllocSize,
					       FALSE, pAd->TxBufSpace[num].AllocVa,
					       pAd->TxBufSpace[num].AllocPa);
		}
		NdisZeroMemory(&pAd->TxBufSpace[num], sizeof(RTMP_DMABUF));

		desc_ring_free(pAd, &pAd->TxDescRing[num]);
	}

	DBGPRINT(RT_DEBUG_TRACE, ("<-- RTMPFreeTxRxRingMemory\n"));
}


NDIS_STATUS RTMPInitTxRxRingMemory(RTMP_ADAPTER *pAd)
{
	INT num, index;
	ULONG RingBasePaHigh, RingBasePaLow;
	VOID *RingBaseVa;
	RTMP_TX_RING *pTxRing;
	RTMP_RX_RING *pRxRing;
	RTMP_DMABUF *pDmaBuf, *pDescRing;
	RTMP_DMACB *dma_cb;
	PNDIS_PACKET pPacket;
	TXD_STRUC *pTxD;
	RXD_STRUC *pRxD;
	ULONG ErrorValue = 0;
	NDIS_STATUS Status = NDIS_STATUS_SUCCESS;


	/* Init the CmdQ and CmdQLock */
	NdisAllocateSpinLock(pAd, &pAd->CmdQLock);
	NdisAcquireSpinLock(&pAd->CmdQLock);
	RTInitializeCmdQ(&pAd->CmdQ);
	NdisReleaseSpinLock(&pAd->CmdQLock);

	/* Initialize All Tx Ring Descriptors and associated buffer memory */
	/* (5 TX rings = 4 ACs + 1 HCCA) */
	for (num = 0; num < NUM_OF_TX_RING; num++) {
		ULONG BufBasePaHigh, BufBasePaLow;
		VOID *BufBaseVa;

		/* memory zero the  Tx ring descriptor's memory */
		pDescRing = &pAd->TxDescRing[num];
		NdisZeroMemory(pDescRing->AllocVa, pDescRing->AllocSize);
		/* Save PA & VA for further operation */
		RingBasePaHigh = RTMP_GetPhysicalAddressHigh(pDescRing->AllocPa);
		RingBasePaLow = RTMP_GetPhysicalAddressLow(pDescRing->AllocPa);
		RingBaseVa = pDescRing->AllocVa;

		/* Zero init all 1st TXBuf's memory for this TxRing */
		NdisZeroMemory(pAd->TxBufSpace[num].AllocVa, pAd->TxBufSpace[num].AllocSize);
		/* Save PA & VA for further operation */
		BufBasePaHigh = RTMP_GetPhysicalAddressHigh(pAd->TxBufSpace[num].AllocPa);
		BufBasePaLow = RTMP_GetPhysicalAddressLow(pAd->TxBufSpace[num].AllocPa);
		BufBaseVa = pAd->TxBufSpace[num].AllocVa;

		/* linking Tx Ring Descriptor and associated buffer memory */
		pTxRing = &pAd->TxRing[num];
		for (index = 0; index < TX_RING_SIZE; index++) {
			dma_cb = &pTxRing->Cell[index];
			dma_cb->pNdisPacket = NULL;
			dma_cb->pNextNdisPacket = NULL;
			/* Init Tx Ring Size, Va, Pa variables */
			dma_cb->AllocSize = TXD_SIZE;
			dma_cb->AllocVa = RingBaseVa;
			RTMP_SetPhysicalAddressHigh(dma_cb->AllocPa, RingBasePaHigh);
			RTMP_SetPhysicalAddressLow(dma_cb->AllocPa, RingBasePaLow);

			/* Setup Tx Buffer size & address. only 802.11 header will store in this space */
			pDmaBuf = &dma_cb->DmaBuf;
			pDmaBuf->AllocSize = TX_DMA_1ST_BUFFER_SIZE;
			pDmaBuf->AllocVa = BufBaseVa;
			RTMP_SetPhysicalAddressHigh(pDmaBuf->AllocPa, BufBasePaHigh);
			RTMP_SetPhysicalAddressLow(pDmaBuf->AllocPa, BufBasePaLow);

			/* link the pre-allocated TxBuf to TXD */
			pTxD = (PTXD_STRUC) dma_cb->AllocVa;
			pTxD->SDPtr0 = BufBasePaLow;
			/* advance to next ring descriptor address */
			pTxD->DMADONE = 1;
#ifdef RT_BIG_ENDIAN
			RTMPDescriptorEndianChange((PUCHAR) pTxD, TYPE_TXD);
#endif				/* endif */

			/* flush dcache if no consistent memory is supported */
			RTMP_DCACHE_FLUSH(pTxD, dma_cb->AllocSize);

			RingBasePaLow += TXD_SIZE;
			RingBaseVa = (PUCHAR) RingBaseVa + TXD_SIZE;

			/* advance to next TxBuf address */
			BufBasePaLow += TX_DMA_1ST_BUFFER_SIZE;
			BufBaseVa = (PUCHAR) BufBaseVa + TX_DMA_1ST_BUFFER_SIZE;
		}
		DBGPRINT(RT_DEBUG_TRACE, ("TxRing[%d]: total %d entry initialized\n", num, index));
	}

	/* Initialize MGMT Ring and associated buffer memory */
	pDescRing = &pAd->MgmtDescRing;
	RingBasePaHigh = RTMP_GetPhysicalAddressHigh(pDescRing->AllocPa);
	RingBasePaLow = RTMP_GetPhysicalAddressLow(pDescRing->AllocPa);
	RingBaseVa = pDescRing->AllocVa;
	NdisZeroMemory(pDescRing->AllocVa, pDescRing->AllocSize);
	for (index = 0; index < MGMT_RING_SIZE; index++) {
		dma_cb = &pAd->MgmtRing.Cell[index];
		dma_cb->pNdisPacket = NULL;
		dma_cb->pNextNdisPacket = NULL;
		/* Init MGMT Ring Size, Va, Pa variables */
		dma_cb->AllocSize = TXD_SIZE;
		dma_cb->AllocVa = RingBaseVa;
		RTMP_SetPhysicalAddressHigh(dma_cb->AllocPa, RingBasePaHigh);
		RTMP_SetPhysicalAddressLow(dma_cb->AllocPa, RingBasePaLow);

		/* Offset to next ring descriptor address */
		RingBasePaLow += TXD_SIZE;
		RingBaseVa = (PUCHAR) RingBaseVa + TXD_SIZE;

		/* link the pre-allocated TxBuf to TXD */
		pTxD = (PTXD_STRUC) dma_cb->AllocVa;
		pTxD->DMADONE = 1;
#ifdef RT_BIG_ENDIAN
		RTMPDescriptorEndianChange((PUCHAR) pTxD, TYPE_TXD);
#endif				/* endif */

		/* flush dcache if no consistent memory is supported */
		RTMP_DCACHE_FLUSH(pTxD, dma_cb->AllocSize);

		/* no pre-allocated buffer required in MgmtRing for scatter-gather case */
	}

#ifdef CONFIG_ANDES_SUPPORT
	/* Initialize CTRL Ring and associated buffer memory */
	pDescRing = &pAd->CtrlDescRing;
	RingBasePaHigh = RTMP_GetPhysicalAddressHigh(pDescRing->AllocPa);
	RingBasePaLow = RTMP_GetPhysicalAddressLow(pDescRing->AllocPa);
	RingBaseVa = pDescRing->AllocVa;
	NdisZeroMemory(pDescRing->AllocVa, pDescRing->AllocSize);
	for (index = 0; index < MGMT_RING_SIZE; index++) {
		dma_cb = &pAd->CtrlRing.Cell[index];
		dma_cb->pNdisPacket = NULL;
		dma_cb->pNextNdisPacket = NULL;
		/* Init Ctrl Ring Size, Va, Pa variables */
		dma_cb->AllocSize = TXD_SIZE;
		dma_cb->AllocVa = RingBaseVa;
		RTMP_SetPhysicalAddressHigh(dma_cb->AllocPa, RingBasePaHigh);
		RTMP_SetPhysicalAddressLow(dma_cb->AllocPa, RingBasePaLow);

		/* Offset to next ring descriptor address */
		RingBasePaLow += TXD_SIZE;
		RingBaseVa = (PUCHAR) RingBaseVa + TXD_SIZE;

		/* link the pre-allocated TxBuf to TXD */
		pTxD = (PTXD_STRUC) dma_cb->AllocVa;
		pTxD->DMADONE = 1;

#ifdef RT_BIG_ENDIAN
		RTMPDescriptorEndianChange((PUCHAR) pTxD, TYPE_TXD);
#endif				/* endif */

		/* flush dcache if no consistent memory is supported */
		RTMP_DCACHE_FLUSH(pTxD, dma_cb->AllocSize);

		/* no pre-allocated buffer required in CtrlRing for scatter-gather case */
	}
#endif				/* CONFIG_ANDES_SUPPORT */

	/* Initialize Rx Ring and associated buffer memory */
	for (num = 0; num < NUM_OF_RX_RING; num++) {
		pDescRing = &pAd->RxDescRing[num];
		pRxRing = &pAd->RxRing[num];

		NdisZeroMemory(pDescRing->AllocVa, pDescRing->AllocSize);
		DBGPRINT(RT_DEBUG_OFF, ("RX[%d] DESC %p size = %ld\n",
					num, pDescRing->AllocVa, pDescRing->AllocSize));

		/* Save PA & VA for further operation */
		RingBasePaHigh = RTMP_GetPhysicalAddressHigh(pDescRing->AllocPa);
		RingBasePaLow = RTMP_GetPhysicalAddressLow(pDescRing->AllocPa);
		RingBaseVa = pDescRing->AllocVa;

		/* Linking Rx Ring and associated buffer memory */
		for (index = 0; index < RX_RING_SIZE; index++) {
			dma_cb = &pRxRing->Cell[index];
			/* Init RX Ring Size, Va, Pa variables */
			dma_cb->AllocSize = RXD_SIZE;
			dma_cb->AllocVa = RingBaseVa;
			RTMP_SetPhysicalAddressHigh(dma_cb->AllocPa, RingBasePaHigh);
			RTMP_SetPhysicalAddressLow(dma_cb->AllocPa, RingBasePaLow);

			/* Offset to next ring descriptor address */
			RingBasePaLow += RXD_SIZE;
			RingBaseVa = (PUCHAR) RingBaseVa + RXD_SIZE;

			/* Setup Rx associated Buffer size & allocate share memory */
			pDmaBuf = &dma_cb->DmaBuf;
			pDmaBuf->AllocSize = RX_BUFFER_AGGRESIZE;
			pPacket = RTMP_AllocateRxPacketBuffer(pAd,
							      (pAd->infType ==
							       RTMP_DEV_INF_RBUS) ? NULL
							      : ((POS_COOKIE) (pAd->OS_Cookie))->
							      pci_dev, pDmaBuf->AllocSize, FALSE,
							      &pDmaBuf->AllocVa, &pDmaBuf->AllocPa);

			/* keep allocated rx packet */
			dma_cb->pNdisPacket = pPacket;

			/* Error handling */
			if (pDmaBuf->AllocVa == NULL) {
				ErrorValue = ERRLOG_OUT_OF_SHARED_MEMORY;
				DBGPRINT_ERR(("Failed to allocate RxRing's 1st buffer\n"));
				Status = NDIS_STATUS_RESOURCES;
				break;
			}

			/* Zero init this memory block */
			NdisZeroMemory(pDmaBuf->AllocVa, pDmaBuf->AllocSize);

			/* Write RxD buffer address & allocated buffer length */
			pRxD = (PRXD_STRUC) dma_cb->AllocVa;
			pRxD->SDP0 = RTMP_GetPhysicalAddressLow(pDmaBuf->AllocPa);
			pRxD->DDONE = 0;
			pRxD->SDL0 = pDmaBuf->AllocSize;

#ifdef RT_BIG_ENDIAN
			RTMPDescriptorEndianChange((PUCHAR) pRxD, TYPE_RXD);
#endif				/* endif */

			/* flush dcache if no consistent memory is supported */
			RTMP_DCACHE_FLUSH(pRxD, dma_cb->AllocSize);
		}
	}

	NdisZeroMemory(&pAd->FragFrame, sizeof(FRAGMENT_FRAME));
	pAd->FragFrame.pFragPacket = RTMP_AllocateFragPacketBuffer(pAd, RX_BUFFER_NORMSIZE);
	if (pAd->FragFrame.pFragPacket == NULL)
		Status = NDIS_STATUS_RESOURCES;

	/* Initialize all transmit related software queues */
	for (index = 0; index < NUM_OF_TX_RING; index++) {
		InitializeQueueHeader(&pAd->TxSwQueue[index]);
		/* Init TX rings index pointer */
		pAd->TxRing[index].TxSwFreeIdx = 0;
		pAd->TxRing[index].TxCpuIdx = 0;
	}

	/* Init RX Ring index pointer */
	for (index = 0; index < NUM_OF_RX_RING; index++) {
		pAd->RxRing[index].RxSwReadIdx = 0;
		pAd->RxRing[index].RxCpuIdx = RX_RING_SIZE - 1;
	}

	/* init MGMT ring index pointer */
	pAd->MgmtRing.TxSwFreeIdx = 0;
	pAd->MgmtRing.TxCpuIdx = 0;

#ifdef CONFIG_ANDES_SUPPORT
	/* init CTRL ring index pointer */
	pAd->CtrlRing.TxSwFreeIdx = 0;
	pAd->CtrlRing.TxCpuIdx = 0;
#endif				/* CONFIG_ANDES_SUPPORT */

	pAd->PrivateInfo.TxRingFullCnt = 0;

	return Status;

}


/*
 *	========================================================================
 *
 *	Routine Description:
 *		Allocate DMA memory blocks for send, receive
 *
 *	Arguments:
 *		Adapter		Pointer to our adapter
 *
 *	Return Value:
 *		NDIS_STATUS_SUCCESS
 *		NDIS_STATUS_FAILURE
 *		NDIS_STATUS_RESOURCES
 *
 *	IRQL = PASSIVE_LEVEL
 *
 *	Note:
 *
 *	========================================================================
 */
NDIS_STATUS RTMPAllocTxRxRingMemory(RTMP_ADAPTER *pAd)
{
	NDIS_STATUS Status = NDIS_STATUS_SUCCESS;
	INT num;
	ULONG ErrorValue = 0;
	VOID *pci_dev =
	    (pAd->infType == RTMP_DEV_INF_RBUS) ? NULL : ((POS_COOKIE) (pAd->OS_Cookie))->pci_dev;

	DBGPRINT(RT_DEBUG_TRACE, ("-->RTMPAllocTxRxRingMemory\n"));
	do {
		/*
		 * Allocate all ring descriptors, include TxD, RxD, MgmtD.
		 * Although each size is different, to prevent cacheline and alignment
		 * issue, I intentional set them all to 64 bytes.
		 */
		for (num = 0; num < NUM_OF_TX_RING; num++) {
			/* Allocate Tx ring descriptor's memory (5 TX rings = 4 ACs + 1 HCCA) */
			desc_ring_alloc(pAd, &pAd->TxDescRing[num], TX_RING_SIZE * TXD_SIZE);
			if (pAd->TxDescRing[num].AllocVa == NULL) {
				Status = NDIS_STATUS_RESOURCES;
				break;
			}
			DBGPRINT(RT_DEBUG_TRACE, ("TxRing[%d]: total %d bytes allocated\n",
						  num, (INT) pAd->TxDescRing[num].AllocSize));

			/* Allocate all 1st TXBuf's memory for this TxRing */
			pAd->TxBufSpace[num].AllocSize = TX_RING_SIZE * TX_DMA_1ST_BUFFER_SIZE;
			RTMP_AllocateFirstTxBuffer(pci_dev,
						   num,
						   pAd->TxBufSpace[num].AllocSize,
						   FALSE,
						   &pAd->TxBufSpace[num].AllocVa,
						   &pAd->TxBufSpace[num].AllocPa);

			if (pAd->TxBufSpace[num].AllocVa == NULL) {
				ErrorValue = ERRLOG_OUT_OF_SHARED_MEMORY;
				DBGPRINT_ERR(("Failed to allocate a big buffer\n"));
				Status = NDIS_STATUS_RESOURCES;
				break;
			}
		}
		if (Status == NDIS_STATUS_RESOURCES)
			break;


		/* Alloc MGMT ring desc buffer except Tx ring allocated eariler */
		desc_ring_alloc(pAd, &pAd->MgmtDescRing, MGMT_RING_SIZE * TXD_SIZE);
		if (pAd->MgmtDescRing.AllocVa == NULL) {
			Status = NDIS_STATUS_RESOURCES;
			break;
		}
		DBGPRINT(RT_DEBUG_TRACE, ("MGMT Ring: total %d bytes allocated\n",
					  (INT) pAd->MgmtDescRing.AllocSize));

#ifdef CONFIG_ANDES_SUPPORT
		/* Alloc CTRL ring desc buffer except Tx ring allocated eariler */
		desc_ring_alloc(pAd, &pAd->CtrlDescRing, MGMT_RING_SIZE * TXD_SIZE);
		if (pAd->CtrlDescRing.AllocVa == NULL) {
			Status = NDIS_STATUS_RESOURCES;
			break;
		}
		DBGPRINT(RT_DEBUG_TRACE, ("CTRL Ring: total %d bytes allocated\n",
					  (INT) pAd->CtrlDescRing.AllocSize));
#endif				/* CONFIG_ANDES_SUPPORT */

		/* Alloc RX ring desc memory except Tx ring allocated eariler */
		for (num = 0; num < NUM_OF_RX_RING; num++) {
			desc_ring_alloc(pAd, &pAd->RxDescRing[num], RX_RING_SIZE * RXD_SIZE);
			if (pAd->RxDescRing[num].AllocVa == NULL) {
				Status = NDIS_STATUS_RESOURCES;
				break;
			}
			DBGPRINT(RT_DEBUG_TRACE, ("Rx[%d] Ring: total %d bytes allocated\n",
						  num, (INT) pAd->RxDescRing[num].AllocSize));
		}
	} while (FALSE);


	if (Status != NDIS_STATUS_SUCCESS) {
		/* Log error inforamtion */
		NdisWriteErrorLogEntry(pAd->AdapterHandle,
				       NDIS_ERROR_CODE_OUT_OF_RESOURCES, 1, ErrorValue);
	}

	DBGPRINT_S(Status, ("<-- RTMPAllocTxRxRingMemory, Status=%x\n", Status));
	return Status;
}


VOID AsicInitTxRxRing(RTMP_ADAPTER *pAd)
{
#ifdef RLT_MAC
	if (pAd->chipCap.hif_type == HIF_RLT)
		rlt_asic_init_txrx_ring(pAd);
#endif				/* RLT_MAC */

#ifdef RTMP_MAC
	if (pAd->chipCap.hif_type == HIF_RTMP)
		rtmp_asic_init_txrx_ring(pAd);
#endif				/* RTMP_MAC */
}


/*
 *	========================================================================
 *
 *	Routine Description:
 *		Reset NIC Asics. Call after rest DMA. So reset TX_CTX_IDX to zero.
 *
 *	Arguments:
 *		Adapter					Pointer to our adapter
 *
 *	Return Value:
 *		None
 *
 *	IRQL = PASSIVE_LEVEL
 *	IRQL = DISPATCH_LEVEL
 *
 *	Note:
 *		Reset NIC to initial state AS IS system boot up time.
 *
 *	========================================================================
 */
VOID RTMPRingCleanUp(RTMP_ADAPTER *pAd, UCHAR RingType)
{
	TXD_STRUC *pTxD;
	RXD_STRUC *pRxD;
#ifdef RT_BIG_ENDIAN
	TXD_STRUC *pDestTxD, TxD;
	RXD_STRUC *pDestRxD, RxD;
#endif				/* RT_BIG_ENDIAN */
	QUEUE_ENTRY *pEntry;
	PNDIS_PACKET pPacket;
	RTMP_TX_RING *pTxRing;
	ULONG IrqFlags;
	int i, ring_id;

	/* We have to clean all descriptors in case some error happened with reset */
	DBGPRINT(RT_DEBUG_TRACE,
		 ("RTMPRingCleanUp(RingIdx=%d, Pending-NDIS=%ld)\n", RingType,
		  pAd->RalinkCounters.PendingNdisPacketCount));
	switch (RingType) {
	case QID_AC_BK:
	case QID_AC_BE:
	case QID_AC_VI:
	case QID_AC_VO:
	case QID_HCCA:

		pTxRing = &pAd->TxRing[RingType];

		RTMP_IRQ_LOCK(&pAd->irq_lock, IrqFlags);
		for (i = 0; i < TX_RING_SIZE; i++) {	/* We have to scan all TX ring */
			pTxD = (PTXD_STRUC) pTxRing->Cell[i].AllocVa;

			pPacket = (PNDIS_PACKET) pTxRing->Cell[i].pNdisPacket;
			/* release scatter-and-gather NDIS_PACKET */
			if (pPacket) {
				RELEASE_NDIS_PACKET(pAd, pPacket, NDIS_STATUS_FAILURE);
				pTxRing->Cell[i].pNdisPacket = NULL;
			}

			pPacket = (PNDIS_PACKET) pTxRing->Cell[i].pNextNdisPacket;
			/* release scatter-and-gather NDIS_PACKET */
			if (pPacket) {
				RELEASE_NDIS_PACKET(pAd, pPacket, NDIS_STATUS_FAILURE);
				pTxRing->Cell[i].pNextNdisPacket = NULL;
			}
		}

		RTMP_IO_READ32(pAd, pTxRing->hw_didx_addr, &pTxRing->TxDmaIdx);
		pTxRing->TxSwFreeIdx = pTxRing->TxDmaIdx;
		pTxRing->TxCpuIdx = pTxRing->TxDmaIdx;
		RTMP_IO_WRITE32(pAd, pTxRing->hw_cidx_addr, pTxRing->TxCpuIdx);

		RTMP_IRQ_UNLOCK(&pAd->irq_lock, IrqFlags);

		RTMP_IRQ_LOCK(&pAd->irq_lock, IrqFlags);
		while (pAd->TxSwQueue[RingType].Head != NULL) {
			pEntry = RemoveHeadQueue(&pAd->TxSwQueue[RingType]);
			pPacket = QUEUE_ENTRY_TO_PACKET(pEntry);
			RELEASE_NDIS_PACKET(pAd, pPacket, NDIS_STATUS_FAILURE);
			DBGPRINT(RT_DEBUG_TRACE,
				 ("Release 1 NDIS packet from s/w backlog queue\n"));
		}
		RTMP_IRQ_UNLOCK(&pAd->irq_lock, IrqFlags);
		break;

	case QID_MGMT:
		RTMP_IRQ_LOCK(&pAd->MgmtRingLock, IrqFlags);
		for (i = 0; i < MGMT_RING_SIZE; i++) {
#ifdef RT_BIG_ENDIAN
			pDestTxD = (PTXD_STRUC) pAd->MgmtRing.Cell[i].AllocVa;
			TxD = *pDestTxD;
			pTxD = &TxD;
			RTMPDescriptorEndianChange((PUCHAR) pTxD, TYPE_TXD);
#else
			pTxD = (PTXD_STRUC) pAd->MgmtRing.Cell[i].AllocVa;
#endif				/* RT_BIG_ENDIAN */

			pPacket = (PNDIS_PACKET) pAd->MgmtRing.Cell[i].pNdisPacket;
			/* rlease scatter-and-gather NDIS_PACKET */
			if (pPacket) {
				PCI_UNMAP_SINGLE(pAd, pTxD->SDPtr0, pTxD->SDLen0,
						 RTMP_PCI_DMA_TODEVICE);
				RELEASE_NDIS_PACKET(pAd, pPacket, NDIS_STATUS_FAILURE);
			}
			pAd->MgmtRing.Cell[i].pNdisPacket = NULL;

			pPacket = (PNDIS_PACKET) pAd->MgmtRing.Cell[i].pNextNdisPacket;
			/* release scatter-and-gather NDIS_PACKET */
			if (pPacket) {
				PCI_UNMAP_SINGLE(pAd, pTxD->SDPtr1, pTxD->SDLen1,
						 RTMP_PCI_DMA_TODEVICE);
				RELEASE_NDIS_PACKET(pAd, pPacket, NDIS_STATUS_FAILURE);
			}
			pAd->MgmtRing.Cell[i].pNextNdisPacket = NULL;

#ifdef RT_BIG_ENDIAN
			RTMPDescriptorEndianChange((PUCHAR) pTxD, TYPE_TXD);
			WriteBackToDescriptor((PUCHAR) pDestTxD, (PUCHAR) pTxD, FALSE, TYPE_TXD);
#endif				/* RT_BIG_ENDIAN */
		}

		RTMP_IO_READ32(pAd, pAd->MgmtRing.hw_didx_addr, &pAd->MgmtRing.TxDmaIdx);
		pAd->MgmtRing.TxSwFreeIdx = pAd->MgmtRing.TxDmaIdx;
		pAd->MgmtRing.TxCpuIdx = pAd->MgmtRing.TxDmaIdx;
		RTMP_IO_WRITE32(pAd, pAd->MgmtRing.hw_cidx_addr, pAd->MgmtRing.TxCpuIdx);

		RTMP_IRQ_UNLOCK(&pAd->MgmtRingLock, IrqFlags);
		pAd->RalinkCounters.MgmtRingFullCount = 0;
		break;

	case QID_RX:
		for (ring_id = 0; ring_id < NUM_OF_RX_RING; ring_id++) {
			RTMP_RX_RING *pRxRing;
			NDIS_SPIN_LOCK *lock;

			pRxRing = &pAd->RxRing[ring_id];
			lock = &pAd->RxRingLock[ring_id];

			RTMP_IRQ_LOCK(lock, IrqFlags);
			for (i = 0; i < RX_RING_SIZE; i++) {
#ifdef RT_BIG_ENDIAN
				pDestRxD = (PRXD_STRUC) pRxRing->Cell[i].AllocVa;
				RxD = *pDestRxD;
				pRxD = &RxD;
				RTMPDescriptorEndianChange((PUCHAR) pRxD, TYPE_RXD);
#else
				/* Point to Rx indexed rx ring descriptor */
				pRxD = (PRXD_STRUC) pRxRing->Cell[i].AllocVa;
#endif				/* RT_BIG_ENDIAN */

				pRxD->DDONE = 0;

#ifdef RT_BIG_ENDIAN
				RTMPDescriptorEndianChange((PUCHAR) pRxD, TYPE_RXD);
				WriteBackToDescriptor((PUCHAR) pDestRxD, (PUCHAR) pRxD, FALSE,
						      TYPE_RXD);
#endif				/* RT_BIG_ENDIAN */
			}

			RTMP_IO_READ32(pAd, pRxRing->hw_didx_addr, &pRxRing->RxDmaIdx);
			pRxRing->RxSwReadIdx = pRxRing->RxDmaIdx;
			pRxRing->RxCpuIdx =
			    ((pRxRing->RxDmaIdx ==
			      0) ? (RX_RING_SIZE - 1) : (pRxRing->RxDmaIdx - 1));
			RTMP_IO_WRITE32(pAd, pRxRing->hw_cidx_addr, pRxRing->RxCpuIdx);

			RTMP_IRQ_UNLOCK(lock, IrqFlags);
		}
		break;

#ifdef CONFIG_ANDES_SUPPORT
	case QID_CTRL:
		RTMP_IRQ_LOCK(&pAd->CtrlRingLock, IrqFlags);

		for (i = 0; i < MGMT_RING_SIZE; i++) {
#ifdef RT_BIG_ENDIAN
			pDestTxD = (PTXD_STRUC) pAd->CtrlRing.Cell[i].AllocVa;
			TxD = *pDestTxD;
			pTxD = &TxD;
			RTMPDescriptorEndianChange((PUCHAR) pTxD, TYPE_TXD);
#else
			pTxD = (PTXD_STRUC) pAd->CtrlRing.Cell[i].AllocVa;
#endif				/* RT_BIG_ENDIAN */

			pPacket = (PNDIS_PACKET) pAd->CtrlRing.Cell[i].pNdisPacket;
			/* rlease scatter-and-gather NDIS_PACKET */
			if (pPacket) {
				PCI_UNMAP_SINGLE(pAd, pTxD->SDPtr0, pTxD->SDLen0,
						 RTMP_PCI_DMA_TODEVICE);
				RELEASE_NDIS_PACKET(pAd, pPacket, NDIS_STATUS_FAILURE);
			}
			pAd->CtrlRing.Cell[i].pNdisPacket = NULL;

			pPacket = (PNDIS_PACKET) pAd->CtrlRing.Cell[i].pNextNdisPacket;
			/* release scatter-and-gather NDIS_PACKET */
			if (pPacket) {
				PCI_UNMAP_SINGLE(pAd, pTxD->SDPtr1, pTxD->SDLen1,
						 RTMP_PCI_DMA_TODEVICE);
				RELEASE_NDIS_PACKET(pAd, pPacket, NDIS_STATUS_FAILURE);
			}
			pAd->CtrlRing.Cell[i].pNextNdisPacket = NULL;

#ifdef RT_BIG_ENDIAN
			RTMPDescriptorEndianChange((PUCHAR) pTxD, TYPE_TXD);
			WriteBackToDescriptor((PUCHAR) pDestTxD, (PUCHAR) pTxD, FALSE, TYPE_TXD);
#endif				/* RT_BIG_ENDIAN */
		}

		RTMP_IO_READ32(pAd, pAd->CtrlRing.hw_didx_addr, &pAd->CtrlRing.TxDmaIdx);
		pAd->CtrlRing.TxSwFreeIdx = pAd->CtrlRing.TxDmaIdx;
		pAd->CtrlRing.TxCpuIdx = pAd->CtrlRing.TxDmaIdx;
		RTMP_IO_WRITE32(pAd, pAd->CtrlRing.hw_cidx_addr, pAd->CtrlRing.TxCpuIdx);

		RTMP_IRQ_UNLOCK(&pAd->CtrlRingLock, IrqFlags);
		break;
#endif				/* CONFIG_ANDES_SUPPORT */
	default:
		break;
	}
}


 /***************************************************************************
  *
  *	register related procedures.
  *
  *************************************************************************
  */
/*
 *========================================================================
 *Routine Description:
 *  Disable DMA.
 *
 * Arguments:
 *	*pAd				the raxx interface data pointer
 *
 * Return Value:
 *	None
 *
 * Note:
 *========================================================================
 */
VOID RT28XXDMADisable(RTMP_ADAPTER *pAd)
{
	WPDMA_GLO_CFG_STRUC GloCfg;

	RTMP_IO_READ32(pAd, WPDMA_GLO_CFG, &GloCfg.word);
	GloCfg.word &= 0xff0;
	GloCfg.field.EnTXWriteBackDDONE = 1;
	RTMP_IO_WRITE32(pAd, WPDMA_GLO_CFG, GloCfg.word);
}


/*
 *========================================================================
 *Routine Description:
 *  Enable DMA.
 *
 * Arguments:
 *	*pAd				the raxx interface data pointer
 *
 * Return Value:
 *	None
 *
 * Note:
 *========================================================================
 */
VOID RT28XXDMAEnable(RTMP_ADAPTER *pAd)
{
	WPDMA_GLO_CFG_STRUC GloCfg;

	RTMP_IO_WRITE32(pAd, MAC_SYS_CTRL, 0x4);
	AsicWaitPDMAIdle(pAd, 200, 1000);

	RtmpusecDelay(50);

	RTMP_IO_READ32(pAd, WPDMA_GLO_CFG, &GloCfg.word);
	GloCfg.field.EnTXWriteBackDDONE = 1;
	GloCfg.field.WPDMABurstSIZE = pAd->chipCap.WPDMABurstSIZE;
	GloCfg.field.EnableRxDMA = 1;
	GloCfg.field.EnableTxDMA = 1;

	RTMP_IO_WRITE32(pAd, WPDMA_GLO_CFG, GloCfg.word);
	DBGPRINT(RT_DEBUG_TRACE,
		 ("<== WRITE DMA offset 0x208 = 0x%x, WPDMABurstSIZE = %d\n", GloCfg.word,
		  pAd->chipCap.WPDMABurstSIZE));
}


BOOLEAN AsicCheckCommanOk(RTMP_ADAPTER *pAd, UCHAR Command)
{
	UINT32 CmdStatus = 0, CID = 0, i;
	UINT32 ThisCIDMask = 0;
#ifdef SPECIFIC_BCN_BUF_SUPPORT
	ULONG IrqFlags = 0;
#endif				/* SPECIFIC_BCN_BUF_SUPPORT */

#ifdef RT65xx
	if (IS_RT65XX(pAd))
		return TRUE;
#endif				/* RT65xx */

#ifdef MT7601
	if (IS_MT7601(pAd))
		return TRUE;
#endif				/* MT7601 */

#ifdef SPECIFIC_BCN_BUF_SUPPORT
	RTMP_MAC_SHR_MSEL_PROTECT_LOCK(pAd, IrqFlags);
#endif				/* SPECIFIC_BCN_BUF_SUPPORT */
	i = 0;
	do {
		RTMP_IO_READ32(pAd, H2M_MAILBOX_CID, &CID);
		/* Find where the command is. Because this is randomly specified by firmware. */
		if ((CID & CID0MASK) == Command) {
			ThisCIDMask = CID0MASK;
			break;
		} else if ((((CID & CID1MASK) >> 8) & 0xff) == Command) {
			ThisCIDMask = CID1MASK;
			break;
		} else if ((((CID & CID2MASK) >> 16) & 0xff) == Command) {
			ThisCIDMask = CID2MASK;
			break;
		} else if ((((CID & CID3MASK) >> 24) & 0xff) == Command) {
			ThisCIDMask = CID3MASK;
			break;
		}

		RtmpusecDelay(100);
		i++;
	} while (i < 200);

	/* Get CommandStatus Value */
	RTMP_IO_READ32(pAd, H2M_MAILBOX_STATUS, &CmdStatus);

	/* This command's status is at the same position as command. So AND command position's bitmask to read status.  */
	if (i < 200) {
		/* If Status is 1, the comamnd is success. */
		if (((CmdStatus & ThisCIDMask) == 0x1) || ((CmdStatus & ThisCIDMask) == 0x100)
		    || ((CmdStatus & ThisCIDMask) == 0x10000)
		    || ((CmdStatus & ThisCIDMask) == 0x1000000)) {
			DBGPRINT(RT_DEBUG_TRACE,
				("--> AsicCheckCommanOk CID = 0x%x, CmdStatus= 0x%x\n", CID,
				 CmdStatus));
			RTMP_IO_WRITE32(pAd, H2M_MAILBOX_STATUS, 0xffffffff);
			RTMP_IO_WRITE32(pAd, H2M_MAILBOX_CID, 0xffffffff);
#ifdef SPECIFIC_BCN_BUF_SUPPORT
			RTMP_MAC_SHR_MSEL_PROTECT_UNLOCK(pAd, IrqFlags);
#endif				/* SPECIFIC_BCN_BUF_SUPPORT */
			return TRUE;
		}
		DBGPRINT(RT_DEBUG_TRACE,
			("--> AsicCheckCommanFail1 CID = 0x%x, CmdStatus= 0x%x\n", CID,
			CmdStatus));
	} else {
		DBGPRINT(RT_DEBUG_TRACE,
			("--> AsicCheckCommanFail2 Timeout Command = %d, CmdStatus= 0x%x\n",
			Command, CmdStatus));
	}
	/* Clear Command and Status. */
	RTMP_IO_WRITE32(pAd, H2M_MAILBOX_STATUS, 0xffffffff);
	RTMP_IO_WRITE32(pAd, H2M_MAILBOX_CID, 0xffffffff);
#ifdef SPECIFIC_BCN_BUF_SUPPORT
	RTMP_MAC_SHR_MSEL_PROTECT_UNLOCK(pAd, IrqFlags);
#endif				/* SPECIFIC_BCN_BUF_SUPPORT */
	return FALSE;
}


/*
 *========================================================================
 * Routine Description:
 * Write Beacon buffer to Asic.
 *
 * Arguments:
 *	*pAd				the raxx interface data pointer
 *
 * Return Value:
 *	None
 *
 * Note:
 *========================================================================
 */
VOID RT28xx_UpdateBeaconToAsic(IN RTMP_ADAPTER *pAd,
			       IN INT apidx, IN ULONG FrameLen, IN ULONG UpdatePos)
{
	ULONG CapInfoPos = 0;
	UCHAR *ptr, *ptr_update, *ptr_capinfo;
	UINT i;
	BOOLEAN bBcnReq = FALSE;
	UCHAR bcn_idx = 0;
	UINT8 TXWISize = pAd->chipCap.TXWISize;
#ifdef CONFIG_AP_SUPPORT
	MULTISSID_STRUCT *pMbss;
#endif				/* endif */
	INT wr_bytes = 1;

	if (IS_MT76x2(pAd))
		wr_bytes = 4;

#ifdef MESH_SUPPORT
	if ((apidx >= MIN_NET_DEVICE_FOR_MESH) && (apidx < MIN_NET_DEVICE_FOR_DLS)) {
		bcn_idx = apidx - MIN_NET_DEVICE_FOR_MESH;
		CapInfoPos = pAd->MeshTab.CapabilityInfoLocationInBeacon;
		bBcnReq = MESH_ON(pAd);

		ptr_capinfo = (PUCHAR) & pAd->MeshTab.BeaconBuf[CapInfoPos];
		ptr_update = (PUCHAR) & pAd->MeshTab.BeaconBuf[UpdatePos];
	} else
#endif				/* MESH_SUPPORT */
#ifdef CONFIG_AP_SUPPORT
	if (apidx < pAd->ApCfg.BssidNum && apidx < HW_BEACON_MAX_NUM &&
#ifdef P2P_SUPPORT
		P2P_GO_ON(pAd)
#else
		pAd->OpMode == OPMODE_AP
#endif/* P2P_SUPPORT */
	) {
		/*MULTISSID_STRUCT *pMbss;*/
		pMbss = &pAd->ApCfg.MBSSID[apidx];
		bcn_idx = pMbss->BcnBufIdx;
		CapInfoPos = pMbss->CapabilityInfoLocationInBeacon;
		bBcnReq = BeaconTransmitRequired(pAd, apidx, pMbss);

		ptr_capinfo = (PUCHAR) & pMbss->BeaconBuf[CapInfoPos];
		ptr_update = (PUCHAR) & pMbss->BeaconBuf[UpdatePos];
	} else
#endif				/* CONFIG_AP_SUPPORT */
	{
		DBGPRINT(RT_DEBUG_ERROR, ("%s() : No valid Interface be found.\n", __func__));
		return;
	}

	if (bBcnReq == FALSE) {
		/* when the ra interface is down, do not send its beacon frame */
		/* clear all zero */
		for (i = 0; i < TXWISize; i += 4)
			RTMP_CHIP_UPDATE_BEACON(pAd, pAd->BeaconOffset[bcn_idx] + i, 0x00, 4);
	} else {
		ptr = (PUCHAR) & pAd->BeaconTxWI;

#ifdef RT_BIG_ENDIAN
		RTMPWIEndianChange(pAd, ptr, TYPE_TXWI);
#endif				/* endif */
		for (i = 0; i < TXWISize; i += 4) {	/* 16-byte TXWI field */
			UINT32 longptr =
			    *ptr + (*(ptr + 1) << 8) + (*(ptr + 2) << 16) + (*(ptr + 3) << 24);
			RTMP_CHIP_UPDATE_BEACON(pAd, pAd->BeaconOffset[bcn_idx] + i, longptr, 4);
			ptr += 4;
		}

		/* Update CapabilityInfo in Beacon */
		if (IS_RT6352(pAd)) {
			/* Update CapabilityInfo in Beacon */
			for (i = CapInfoPos; i < (CapInfoPos + 2); i++) {
				RTMP_CHIP_UPDATE_BEACON(pAd,
							pAd->BeaconOffset[bcn_idx] + TXWISize + i,
							*ptr_capinfo, 1);
				ptr_capinfo++;
			}

			if (FrameLen > UpdatePos) {
				for (i = UpdatePos; i < (FrameLen); i++) {
					RTMP_CHIP_UPDATE_BEACON(pAd,
								pAd->BeaconOffset[bcn_idx] +
								TXWISize + i, *ptr_update, 1);
					ptr_update++;
				}
			}
		} else {
			if (wr_bytes > 1)
				CapInfoPos = (CapInfoPos & (~(wr_bytes - 1)));
#ifdef CONFIG_AP_SUPPORT
			ptr_capinfo = (PUCHAR) & pMbss->BeaconBuf[CapInfoPos];
#endif				/* CONFIG_AP_SUPPORT */
			for (i = CapInfoPos; i < (CapInfoPos + 2); i += wr_bytes) {
#ifdef RT_BIG_ENDIAN
				UINT32 longptr =
				    *ptr_capinfo + (*(ptr_capinfo + 1) << 8) +
				    (*(ptr_capinfo + 2) << 16) + (*(ptr_capinfo + 3) << 24);
				RTMP_CHIP_UPDATE_BEACON(pAd,
							pAd->BeaconOffset[bcn_idx] + TXWISize + i,
							longptr, wr_bytes);
#else
				RTMP_CHIP_UPDATE_BEACON(pAd,
							pAd->BeaconOffset[bcn_idx] + TXWISize + i,
							*((UINT32 *) ptr_capinfo), wr_bytes);
#endif				/* RT_BIG_ENDIAN */
				ptr_capinfo += wr_bytes;
			}

			if (FrameLen > UpdatePos) {
				if (wr_bytes > 1)
					UpdatePos = (UpdatePos & (~(wr_bytes - 1)));
#ifdef CONFIG_AP_SUPPORT
				ptr_update = (PUCHAR) & pMbss->BeaconBuf[UpdatePos];
#endif				/* CONFIG_AP_SUPPORT */
				for (i = UpdatePos; i < (FrameLen); i += wr_bytes) {
					UINT32 longptr =
					    *ptr_update + (*(ptr_update + 1) << 8) +
					    (*(ptr_update + 2) << 16) + (*(ptr_update + 3) << 24);
					RTMP_IO_WRITE32(pAd,
							pAd->BeaconOffset[bcn_idx] + TXWISize + i,
							longptr);
					ptr_update += wr_bytes;
				}
			}
		}
	}
}


#ifdef CONFIG_STA_SUPPORT
VOID RT28xxPciStaAsicForceWakeup(RTMP_ADAPTER *pAd, BOOLEAN bFromTx)
{
	AUTO_WAKEUP_STRUC AutoWakeupCfg;

	if (!OPSTATUS_TEST_FLAG(pAd, fOP_STATUS_DOZE))
		return;

	if (OPSTATUS_TEST_FLAG(pAd, fOP_STATUS_WAKEUP_NOW)) {
		DBGPRINT(RT_DEBUG_TRACE, ("waking up now!\n"));
		return;
	}

	OPSTATUS_SET_FLAG(pAd, fOP_STATUS_WAKEUP_NOW);

	/* RTMP_CLEAR_PSFLAG(pAd, fRTMP_PS_GO_TO_SLEEP_NOW); */
#ifdef PCIE_PS_SUPPORT
	if (OPSTATUS_TEST_FLAG(pAd, fOP_STATUS_ADVANCE_POWER_SAVE_PCIE_DEVICE)
	    && pAd->StaCfg.PSControl.field.EnableNewPS == TRUE) {
		/* Support PCIe Advance Power Save */
		if (bFromTx == TRUE && (pAd->Mlme.bPsPollTimerRunning == TRUE)) {
			pAd->Mlme.bPsPollTimerRunning = FALSE;
			RTMPPCIeLinkCtrlValueRestore(pAd, RESTORE_WAKEUP);
			RtmpusecDelay(3000);
			DBGPRINT(RT_DEBUG_TRACE, ("=======AsicForceWakeup===bFromTx\n"));
		}

		AutoWakeupCfg.word = 0;
		RTMP_IO_WRITE32(pAd, AUTO_WAKEUP_CFG, AutoWakeupCfg.word);

		if (RT28xxPciAsicRadioOn(pAd, DOT11POWERSAVE)) {
			RTMP_CHIP_OP *pChipOps = &pAd->chipOps;

			if (pChipOps->AsicReverseRfFromSleepMode)
				pChipOps->AsicReverseRfFromSleepMode(pAd, FALSE);
			else {
				UCHAR rf_channel;

				/* In Radio Off, we turn off RF clk, So now need to call ASICSwitchChannel again. */
				if (INFRA_ON(pAd)
				    && (pAd->CommonCfg.CentralChannel != pAd->CommonCfg.Channel)
				    && (pAd->MlmeAux.HtCapability.HtCapInfo.ChannelWidth == BW_40))
					rf_channel = pAd->CommonCfg.CentralChannel;
				else
					rf_channel = pAd->CommonCfg.Channel;
				AsicSwitchChannel(pAd, rf_channel, FALSE);
				AsicLockChannel(pAd, rf_channel);

			}
		}
#if defined(RT3090) || defined(RT3592) || defined(RT3390) || defined(RT3593) || defined(RT5390) || defined(RT5392) || defined(RT5592)

		/* 3090 MCU Wakeup command needs more time to be stable. */
		/* Before stable, don't issue other MCU command to prevent from firmware error. */
/*Move IS_VERSION_AFTER_F to IS_SUPPORT_PCIE_PS_L3*/
/*Capability mechansim*/

		if (pAd->chipCap.HW_PCIE_PS_L3_ENABLE == TRUE) {
			DBGPRINT(RT_DEBUG_TRACE,
				 ("<==RT28xxPciStaAsicForceWakeup::Release the MCU Lock(3090)\n"));
			RTMP_SEM_LOCK(&pAd->McuCmdLock);
			pAd->brt30xxBanMcuCmd = FALSE;
			RTMP_SEM_UNLOCK(&pAd->McuCmdLock);
		}
#endif				/* defined(RT3090) || defined(RT3592) || defined(RT3390) || defined(RT3593) || defined(RT5390) || defined(RT5392) */
	} else
#endif				/* PCIE_PS_SUPPORT */
	{
		/* PCI, 2860-PCIe */
		DBGPRINT(RT_DEBUG_TRACE,
			 ("<==RT28xxPciStaAsicForceWakeup::Original PCI Power Saving\n"));
		AsicSendCommandToMcu(pAd, 0x31, 0xff, 0x00, 0x02, FALSE);
		AutoWakeupCfg.word = 0;
		RTMP_IO_WRITE32(pAd, AUTO_WAKEUP_CFG, AutoWakeupCfg.word);
	}

	OPSTATUS_CLEAR_FLAG(pAd, fOP_STATUS_DOZE);
	OPSTATUS_CLEAR_FLAG(pAd, fOP_STATUS_WAKEUP_NOW);
	DBGPRINT(RT_DEBUG_TRACE, ("<=======RT28xxPciStaAsicForceWakeup\n"));
}


VOID RT28xxPciStaAsicSleepThenAutoWakeup(IN RTMP_ADAPTER *pAd, IN USHORT TbttNumToNextWakeUp)
{
#ifdef PCIE_PS_SUPPORT
	BOOLEAN brc;
#endif				/* PCIE_PS_SUPPORT */

	if (pAd->StaCfg.bRadio == FALSE) {
		OPSTATUS_CLEAR_FLAG(pAd, fOP_STATUS_DOZE);
		return;
	}
#ifdef PCIE_PS_SUPPORT
	if (OPSTATUS_TEST_FLAG(pAd, fOP_STATUS_ADVANCE_POWER_SAVE_PCIE_DEVICE)
	    && pAd->StaCfg.PSControl.field.EnableNewPS == TRUE) {
		ULONG Now = 0;

		if (OPSTATUS_TEST_FLAG(pAd, fOP_STATUS_WAKEUP_NOW)) {
			DBGPRINT(RT_DEBUG_TRACE, ("waking up now!\n"));
			OPSTATUS_CLEAR_FLAG(pAd, fOP_STATUS_DOZE);
			return;
		}

		NdisGetSystemUpTime(&Now);
		/* If last send NULL fram time is too close to this receiving beacon (within 8ms), don't go to sleep for this DTM. */
		/* Because Some AP can't queuing outgoing frames immediately. */
		if (((pAd->Mlme.LastSendNULLpsmTime + 8) >= Now)
		    && (pAd->Mlme.LastSendNULLpsmTime <= Now)) {
			DBGPRINT(RT_DEBUG_TRACE,
				 ("Now = %lu, LastSendNULLpsmTime=%lu :  RxCountSinceLastNULL = %lu.\n",
				  Now, pAd->Mlme.LastSendNULLpsmTime,
				  pAd->RalinkCounters.RxCountSinceLastNULL));
			return;
		} else if ((pAd->RalinkCounters.RxCountSinceLastNULL > 0)
			   && ((pAd->Mlme.LastSendNULLpsmTime + pAd->CommonCfg.BeaconPeriod) >=
			       Now)) {
			DBGPRINT(RT_DEBUG_TRACE,
				 ("Now = %lu, LastSendNULLpsmTime=%lu: RxCountSinceLastNULL = %lu > 0\n",
				  Now, pAd->Mlme.LastSendNULLpsmTime,
				  pAd->RalinkCounters.RxCountSinceLastNULL));
			return;
		}

		brc = RT28xxPciAsicRadioOff(pAd, DOT11POWERSAVE, TbttNumToNextWakeUp);
		if (brc == TRUE)
			OPSTATUS_SET_FLAG(pAd, fOP_STATUS_DOZE);
	} else
#endif				/* PCIE_PS_SUPPORT */
	{
		AUTO_WAKEUP_STRUC AutoWakeupCfg;
		/* we have decided to SLEEP, so at least do it for a BEACON period.     */
		if (TbttNumToNextWakeUp == 0)
			TbttNumToNextWakeUp = 1;

		AutoWakeupCfg.word = 0;
		RTMP_IO_WRITE32(pAd, AUTO_WAKEUP_CFG, AutoWakeupCfg.word);
		AutoWakeupCfg.field.NumofSleepingTbtt = TbttNumToNextWakeUp - 1;
		AutoWakeupCfg.field.EnableAutoWakeup = 1;
		AutoWakeupCfg.field.AutoLeadTime = 5;
		RTMP_IO_WRITE32(pAd, AUTO_WAKEUP_CFG, AutoWakeupCfg.word);
		AsicSendCommandToMcu(pAd, 0x30, 0xff, 0xff, 0x00, FALSE);	/* send POWER-SAVE command to MCU. Timeout 40us. */
		OPSTATUS_SET_FLAG(pAd, fOP_STATUS_DOZE);
		DBGPRINT(RT_DEBUG_TRACE,
			 ("<-- %s, TbttNumToNextWakeUp=%d\n", __func__, TbttNumToNextWakeUp));
	}

}

#ifdef PCIE_PS_SUPPORT
VOID PsPollWakeExec(IN PVOID SystemSpecific1,
		    IN PVOID FunctionContext, IN PVOID SystemSpecific2, IN PVOID SystemSpecific3)
{
	RTMP_ADAPTER *pAd = (RTMP_ADAPTER *) FunctionContext;
	unsigned long flags;

	DBGPRINT(RT_DEBUG_TRACE, ("-->PsPollWakeExec\n"));

	RTMP_INT_LOCK(&pAd->irq_lock, flags);
	if (pAd->Mlme.bPsPollTimerRunning)
		RTMPPCIeLinkCtrlValueRestore(pAd, RESTORE_WAKEUP);
	pAd->Mlme.bPsPollTimerRunning = FALSE;
	RTMP_INT_UNLOCK(&pAd->irq_lock, flags);
#if defined(RT3090) || defined(RT3592) || defined(RT3390) || defined(RT3593) || defined(RT5390) || defined(RT5392) || defined(RT5592)
	/* For rt30xx power solution 3, Use software timer to wake up in psm. So call */
	/* AsicForceWakeup here instead of handling twakeup interrupt. */
	if (pAd->chipCap.HW_PCIE_PS_L3_ENABLE == TRUE) {
		DBGPRINT(RT_DEBUG_TRACE,
			 ("<--PsPollWakeExec:: calls AsicForceWakeup(pAd, DOT11POWERSAVE) in advance\n"));
		AsicForceWakeup(pAd, DOT11POWERSAVE);
	}
#endif				/* defined(RT3090) || defined(RT3592) || defined(RT3390) || defined(RT3593) || defined(RT5390) || defined(RT5392) */

#ifdef RT3290
	/*
	 * For RT3290, Frank said firmware will not help driver to set register to control
	 * power saving related jobs. So we need do it ourselves.
	 * Besides, for RT3290, Frank will config two power saving modes,
	 * one is mapping to Radio on,
	 * the other is mapping to rt30xxPowerMode = 3
	 *
	 * What we'll do about this??
	 */
	/* TODO: shiang, find a better way to manage power saving related tasks. */
	if (IS_RT3290(pAd))
		AsicForceWakeup(pAd, DOT11POWERSAVE);
#endif				/*RT3290 */
}


VOID RadioOnExec(IN PVOID SystemSpecific1,
		 IN PVOID FunctionContext, IN PVOID SystemSpecific2, IN PVOID SystemSpecific3)
{
	RTMP_ADAPTER *pAd = (RTMP_ADAPTER *) FunctionContext;
	RTMP_CHIP_OP *pChipOps = &pAd->chipOps;
	WPDMA_GLO_CFG_STRUC DmaCfg;
	BOOLEAN Cancelled;

	if (OPSTATUS_TEST_FLAG(pAd, fOP_STATUS_DOZE)) {
		DBGPRINT(RT_DEBUG_TRACE,
			 ("-->RadioOnExec() return on fOP_STATUS_DOZE == TRUE;\n"));
		if (OPSTATUS_TEST_FLAG(pAd, fOP_STATUS_ADVANCE_POWER_SAVE_PCIE_DEVICE)
		    && pAd->StaCfg.PSControl.field.EnableNewPS == TRUE)
			RTMPSetTimer(&pAd->Mlme.RadioOnOffTimer, 10);
		return;
	}

	if (RTMP_TEST_FLAG(pAd, fRTMP_ADAPTER_BSS_SCAN_IN_PROGRESS)) {
		DBGPRINT(RT_DEBUG_TRACE, ("-->RadioOnExec() return on SCAN_IN_PROGRESS;\n"));


		if (OPSTATUS_TEST_FLAG(pAd, fOP_STATUS_ADVANCE_POWER_SAVE_PCIE_DEVICE)
		    && pAd->StaCfg.PSControl.field.EnableNewPS == TRUE)
			RTMPSetTimer(&pAd->Mlme.RadioOnOffTimer, 10);
		return;
	}

	if (OPSTATUS_TEST_FLAG(pAd, fOP_STATUS_ADVANCE_POWER_SAVE_PCIE_DEVICE)
	    && pAd->StaCfg.PSControl.field.EnableNewPS == TRUE) {
		pAd->Mlme.bPsPollTimerRunning = FALSE;
		RTMPCancelTimer(&pAd->Mlme.PsPollTimer, &Cancelled);
	}
#ifdef RT3290
	if (IS_RT3290(pAd) && (pAd->StaCfg.bRadio == TRUE)) {
		AsicForceWakeup(pAd, FROM_TX);

		RTMPRingCleanUp(pAd, QID_AC_BK);
		RTMPRingCleanUp(pAd, QID_AC_BE);
		RTMPRingCleanUp(pAd, QID_AC_VI);
		RTMPRingCleanUp(pAd, QID_AC_VO);
		RTMPRingCleanUp(pAd, QID_HCCA);
		RTMPRingCleanUp(pAd, QID_MGMT);
		RTMPRingCleanUp(pAd, QID_RX);

		/* When PCI clock is off, don't want to service interrupt. So when back to clock on, enable interrupt. */
		RTMP_ASIC_INTERRUPT_ENABLE(pAd);

		RTMP_CLEAR_FLAG(pAd, fRTMP_ADAPTER_IDLE_RADIO_OFF);

		/* Clear Radio off flag */
		RTMP_CLEAR_FLAG(pAd, fRTMP_ADAPTER_RADIO_OFF);

		/* Set LED */
		RTMPSetLED(pAd, LED_RADIO_ON);

		/* TODO: shiang, for RT3290, is a new pcie ps meachansim?  and what's the purpose of bDelayedPhyPowerIndication??? */

	} else
#endif				/* RT3290 */

	if (pAd->StaCfg.bRadio == TRUE) {
		UCHAR rf_channel;

		pAd->bPCIclkOff = FALSE;
		RTMPRingCleanUp(pAd, QID_AC_BK);
		RTMPRingCleanUp(pAd, QID_AC_BE);
		RTMPRingCleanUp(pAd, QID_AC_VI);
		RTMPRingCleanUp(pAd, QID_AC_VO);
		RTMPRingCleanUp(pAd, QID_HCCA);
		RTMPRingCleanUp(pAd, QID_MGMT);
		RTMPRingCleanUp(pAd, QID_RX);

		/* 2. Send wake up command and wait for command done. */
		AsicSendCmdToMcuAndWait(pAd, 0x31, PowerWakeCID, 0x00, 0x02, FALSE);

		/* When PCI clock is off, don't want to service interrupt. So when back to clock on, enable interrupt. */
		RTMP_ASIC_INTERRUPT_ENABLE(pAd);

		/* 3. Enable Tx DMA. */
		RTMP_IO_READ32(pAd, WPDMA_GLO_CFG, &DmaCfg.word);
		DmaCfg.field.EnableTxDMA = 1;
		RTMP_IO_WRITE32(pAd, WPDMA_GLO_CFG, DmaCfg.word);

		/* In Radio Off, we turn off RF clk, So now need to call ASICSwitchChannel again. */
		if (INFRA_ON(pAd) && (pAd->CommonCfg.CentralChannel != pAd->CommonCfg.Channel)
		    && (pAd->MlmeAux.HtCapability.HtCapInfo.ChannelWidth == BW_40))
			rf_channel = pAd->CommonCfg.CentralChannel;
		else
			rf_channel = pAd->CommonCfg.Channel;
		AsicSwitchChannel(pAd, rf_channel, FALSE);
		AsicLockChannel(pAd, rf_channel);

		if (pChipOps->AsicReverseRfFromSleepMode)
			pChipOps->AsicReverseRfFromSleepMode(pAd, FALSE);

#if defined(RT3090) || defined(RT3592) || defined(RT3390) || defined(RT3593) || defined(RT5390) || defined(RT5392) || defined(RT5592) || defined(RT3290)
		/* 3090 MCU Wakeup command needs more time to be stable. */
		/* Before stable, don't issue other MCU command to prevent from firmware error. */
		if (pAd->chipCap.HW_PCIE_PS_L3_ENABLE == TRUE) {
			RTMP_SEM_LOCK(&pAd->McuCmdLock);
			pAd->brt30xxBanMcuCmd = FALSE;
			RTMP_SEM_UNLOCK(&pAd->McuCmdLock);
		}
#endif				/* defined(RT3090) || defined(RT3592) || defined(RT3390) || defined(RT3593) || defined(RT5390) || defined(RT5392) || defined(RT5592) || defined(RT3290) */
		/* Clear Radio off flag */
		RTMP_CLEAR_FLAG(pAd, fRTMP_ADAPTER_RADIO_OFF);

#ifdef LED_CONTROL_SUPPORT
		/* Set LED */
		RTMPSetLED(pAd, LED_RADIO_ON);
#endif				/* LED_CONTROL_SUPPORT */

		if (RtmpPktPmBitCheck(pAd) == FALSE) {
#ifdef RTMP_BBP
			RTMP_BBP_IO_WRITE8_BY_REG_ID(pAd, BBP_R3, pAd->StaCfg.BBPR3);
#endif				/* RTMP_BBP */
		}
	} else {
		RT28xxPciAsicRadioOff(pAd, GUIRADIO_OFF, 0);
	}
}
#endif				/* PCIE_PS_SUPPORT */
#endif				/* CONFIG_STA_SUPPORT */


/*
 *	==========================================================================
 *	Description:
 *		This routine sends command to firmware and turn our chip to wake up mode from power save mode.
 *		Both RadioOn and .11 power save function needs to call this routine.
 *	Input:
 *	Level = GUIRADIO_OFF : call this function is from Radio Off to Radio On.  Need to restore PCI host value.
 *	Level = other value : normal wake up function.
 *
 *	==========================================================================
 */
BOOLEAN RT28xxPciAsicRadioOn(RTMP_ADAPTER *pAd, UCHAR Level)
{
#ifdef CONFIG_STA_SUPPORT
#ifdef PCIE_PS_SUPPORT
	BOOLEAN Cancelled;
#endif				/* PCIE_PS_SUPPORT */
#endif				/* CONFIG_STA_SUPPORT */

	if (pAd->OpMode == OPMODE_AP && Level == DOT11POWERSAVE)
		return FALSE;

#ifdef CONFIG_STA_SUPPORT
#ifdef PCIE_PS_SUPPORT
	if (OPSTATUS_TEST_FLAG(pAd, fOP_STATUS_ADVANCE_POWER_SAVE_PCIE_DEVICE)) {
		if (pAd->StaCfg.PSControl.field.EnableNewPS == TRUE) {
			pAd->Mlme.bPsPollTimerRunning = FALSE;
			RTMPCancelTimer(&pAd->Mlme.PsPollTimer, &Cancelled);
		}
		if ((pAd->StaCfg.PSControl.field.EnableNewPS == TRUE) &&
		    (((Level == GUIRADIO_OFF) || (Level == GUI_IDLE_POWER_SAVE))
		     || (RTMP_TEST_PSFLAG(pAd, fRTMP_PS_SET_PCI_CLK_OFF_COMMAND)))) {
			/* Some chips don't need to delay 6ms, so copy RTMPPCIePowerLinkCtrlRestore */
			/* return condition here. */
			/*
			 * if (((pAd->MACVersion&0xffff0000) != 0x28600000)
			 * && ((pAd->DeviceID == NIC2860_PCIe_DEVICE_ID)
			 * ||(pAd->DeviceID == NIC2790_PCIe_DEVICE_ID)))
			 */
			{
				DBGPRINT(RT_DEBUG_TRACE, ("RT28xxPciAsicRadioOn ()\n"));
				/* 1. Set PCI Link Control in Configuration Space. */
				RTMPPCIeLinkCtrlValueRestore(pAd, RESTORE_WAKEUP);
				RtmpusecDelay(6000);
			}
		}
	}
#if defined(RT3090) || defined(RT3592) || defined(RT3390) || defined(RT3593) || defined(RT5390) || defined(RT5392) || defined(RT5592)
	if (pAd->chipCap.HW_PCIE_PS_L3_ENABLE == FALSE)
#endif				/* defined(RT3090) || defined(RT3592) || defined(RT3390) || defined(RT3593) || defined(RT5390) || defined(RT5392) */
	{
		pAd->bPCIclkOff = FALSE;
		DBGPRINT(RT_DEBUG_TRACE, ("PSM :309xbPCIclkOff == %d\n", pAd->bPCIclkOff));
	}
#endif				/* PCIE_PS_SUPPORT */
#endif				/* CONFIG_STA_SUPPORT */

	/* 2. Send wake up command. */
	AsicSendCommandToMcu(pAd, 0x31, PowerWakeCID, 0x00, 0x02, FALSE);
	pAd->bPCIclkOff = FALSE;
	/* 2-1. wait command ok. */
	AsicCheckCommanOk(pAd, PowerWakeCID);
	RTMP_ASIC_INTERRUPT_ENABLE(pAd);

#ifdef RT5390
#ifdef BT_COEXISTENCE_SUPPORT
	/* TODO: shiang, this is confused me!! why covered by "#ifdef RT5390" but has "IS_RT3592BC8" ? */
	if (IS_RT5390(pAd)) {
		ULONG MACValue = 0;

		/* Eable MAC Rx */
		RTMP_IO_READ32(pAd, MAC_SYS_CTRL, &MACValue);
		MACValue |= 0x8;
		if ((pAd->NicConfig2.field.CoexBit == TRUE) && (!IS_RT5390BC8(pAd))
		    && (!IS_RT3592BC8(pAd))) {
			if (IS_ENABLE_BT_WIFI_ACTIVE_PULL_HIGH_BY_TIMER(pAd)
			    && (pAd->BT_BC_PERMIT_RXWIFI_ACTIVE == TRUE))
				MACValue |= 0x2240;
			else if (IS_ENABLE_WIFI_ACTIVE_PULL_LOW_BY_FORCE(pAd))
				MACValue |= 0x0240;
			else
				MACValue |= 0x1240;
		}
		RTMP_IO_WRITE32(pAd, MAC_SYS_CTRL, MACValue);
	}
#endif				/* BT_COEXISTENCE_SUPPORT */
#endif				/* RT5390 */

	RTMP_CLEAR_FLAG(pAd, fRTMP_ADAPTER_IDLE_RADIO_OFF);
	if (Level == GUI_IDLE_POWER_SAVE) {
		/*2009/06/09: AP and stations need call the following function */
#ifndef RTMP_RBUS_SUPPORT
		/* add by johnli, RF power sequence setup, load RF normal operation-mode setup */

		RTMP_CHIP_OP *pChipOps = &pAd->chipOps;

		if (pChipOps->AsicReverseRfFromSleepMode) {
			pChipOps->AsicReverseRfFromSleepMode(pAd, FALSE);
#ifdef CONFIG_STA_SUPPORT
#ifdef PCIE_PS_SUPPORT
			/* 3090 MCU Wakeup command needs more time to be stable. */
			/* Before stable, don't issue other MCU command to prevent from firmware error. */

			if (pAd->chipCap.HW_PCIE_PS_L3_ENABLE == TRUE) {
				RTMP_SEM_LOCK(&pAd->McuCmdLock);
				pAd->brt30xxBanMcuCmd = FALSE;
				RTMP_SEM_UNLOCK(&pAd->McuCmdLock);
			}
#endif				/* PCIE_PS_SUPPORT */
#endif				/* CONFIG_STA_SUPPORT */

#ifdef RT3593
#ifdef CONFIG_AP_SUPPORT
			IF_DEV_CONFIG_OPMODE_ON_AP(pAd) {
				if (IS_RT3593(pAd)) {
					AsicSwitchChannel(pAd, pAd->CommonCfg.CentralChannel,
							  FALSE);
					AsicLockChannel(pAd, pAd->CommonCfg.CentralChannel);
				}
			}
#endif				/* CONFIG_AP_SUPPORT */
#endif				/* RT3593 */
		} else
#endif				/* RTMP_RBUS_SUPPORT */
		{
			/* In Radio Off, we turn off RF clk, So now need to call ASICSwitchChannel again. */
#ifdef CONFIG_STA_SUPPORT
			IF_DEV_CONFIG_OPMODE_ON_STA(pAd) {
				UCHAR rf_channel;

				if (INFRA_ON(pAd)
				    && (pAd->CommonCfg.CentralChannel != pAd->CommonCfg.Channel)
				    && (pAd->MlmeAux.HtCapability.HtCapInfo.ChannelWidth == BW_40))
					rf_channel = pAd->CommonCfg.CentralChannel;
				else
					rf_channel = pAd->CommonCfg.Channel;
				AsicSwitchChannel(pAd, rf_channel, FALSE);
				AsicLockChannel(pAd, rf_channel);
			}
#endif				/* CONFIG_STA_SUPPORT */
#ifdef CONFIG_AP_SUPPORT
			IF_DEV_CONFIG_OPMODE_ON_AP(pAd) {
				AsicSwitchChannel(pAd, pAd->CommonCfg.CentralChannel, FALSE);
				AsicLockChannel(pAd, pAd->CommonCfg.CentralChannel);
			}
#endif				/* CONFIG_AP_SUPPORT */
		}
	}
	return TRUE;

}


/*
 *	==========================================================================
 *	Description:
 *		This routine sends command to firmware and turn our chip to power save mode.
 *		Both RadioOff and .11 power save function needs to call this routine.
 *	Input:
 *		Level = GUIRADIO_OFF  : GUI Radio Off mode
 *		Level = DOT11POWERSAVE  : 802.11 power save mode
 *		Level = RTMP_HALT  : When Disable device.
 *
 *	==========================================================================
 */
BOOLEAN RT28xxPciAsicRadioOff(IN RTMP_ADAPTER *pAd, IN UCHAR Level, IN USHORT TbttNumToNextWakeUp)
{
#ifdef CONFIG_STA_SUPPORT
#ifdef PCIE_PS_SUPPORT
	UCHAR i;
	ULONG BeaconPeriodTime;
	UINT32 PsPollTime = 0;
	UINT32 TbTTTime = 0;
	BOOLEAN Cancelled;
#endif				/* PCIE_PS_SUPPORT */
#endif				/* CONFIG_STA_SUPPORT */
#if (defined(CONFIG_STA_SUPPORT) && defined(PCIE_PS_SUPPORT)) || defined(RT2860)
	BOOLEAN brc = FALSE;
#endif				/* defined(CONFIG_STA_SUPPORT) || defined(RT2860) */
	UINT32 RxDmaIdx, RxCpuIdx;

	DBGPRINT(RT_DEBUG_TRACE,
		 ("%s ===> Lv= %d, TxCpuIdx = %d, TxDmaIdx = %d. RxCpuIdx = %d, RxDmaIdx = %d.\n",
		  __func__, Level, pAd->TxRing[0].TxCpuIdx, pAd->TxRing[0].TxDmaIdx,
		  pAd->RxRing[0].RxCpuIdx, pAd->RxRing[0].RxDmaIdx));

	if (pAd->OpMode == OPMODE_AP && Level == DOT11POWERSAVE)
		return FALSE;

	if (Level == DOT11POWERSAVE) {
		/* Check Rx DMA busy status, if more than half is occupied, give up this radio off. */
		RTMP_IO_READ32(pAd, pAd->RxRing[0].hw_didx_addr, &RxDmaIdx);
		RTMP_IO_READ32(pAd, pAd->RxRing[0].hw_cidx_addr, &RxCpuIdx);
		if ((RxDmaIdx > RxCpuIdx) && ((RxDmaIdx - RxCpuIdx) > RX_RING_SIZE / 3)) {
			DBGPRINT(RT_DEBUG_TRACE, ("%s(): return1. RxDmaIdx=%d, RxCpuIdx=%d\n",
						  __func__, RxDmaIdx, RxCpuIdx));
			return FALSE;
		} else if ((RxCpuIdx >= RxDmaIdx) && ((RxCpuIdx - RxDmaIdx) < RX_RING_SIZE / 3)) {
			DBGPRINT(RT_DEBUG_TRACE, ("%s(): return2. RxDmaIdx=%d, RxCpuIdx=%d\n",
						  __func__, RxDmaIdx, RxCpuIdx));
			return FALSE;
		}
	}
#if defined(RT5390) || defined(RT35xx)
#ifdef BT_COEXISTENCE_SUPPORT
	if (IS_RT3592BC8(pAd) || IS_RT5390BC8(pAd)) {
		/* Switch to direct mode to save power consumption. */

		UINT32 Value;

		RTMP_IO_READ32(pAd, GPIO_CTRL_CFG, &Value);
		Value &= ~(0x0808);
		Value |= 0x08;

		if (IS_RT5390BC8(pAd)) {
			/*
			 * RT5390 Use GPIO6 and GPIO3 to control antenna diversity
			 * Also make sure GPIO_SWITCH(Function) MAC 0x05DC Bit[6] been enabled.
			 * Here we use GPIO6 instead of EESK.
			 */
			Value &= ~(0x4040);
		} else {
			RTMP_IO_READ32(pAd, E2PROM_CSR, &x);
			x &= ~(EESK);
			RTMP_IO_WRITE32(pAd, E2PROM_CSR, x);
		}
		RTMP_IO_WRITE32(pAd, GPIO_CTRL_CFG, Value);
	}
#endif				/* BT_COEXISTENCE_SUPPORT */
#endif				/* defined(RT5390) || defined(RT35xx) */

	/* Once go into this function, disable tx because don't want too many packets in queue to prevent HW stops. */
	/*pAd->bPCIclkOffDisableTx = TRUE; */
#ifdef CONFIG_STA_SUPPORT
#ifdef PCIE_PS_SUPPORT
	RTMP_SET_PSFLAG(pAd, fRTMP_PS_DISABLE_TX);
	if (OPSTATUS_TEST_FLAG(pAd, fOP_STATUS_ADVANCE_POWER_SAVE_PCIE_DEVICE)
	    && pAd->OpMode == OPMODE_STA && pAd->StaCfg.PSControl.field.EnableNewPS == TRUE) {

		RTMPCancelTimer(&pAd->Mlme.RadioOnOffTimer, &Cancelled);
		RTMPCancelTimer(&pAd->Mlme.PsPollTimer, &Cancelled);

		if (Level == DOT11POWERSAVE) {
			RTMP_IO_READ32(pAd, TBTT_TIMER, &TbTTTime);
			TbTTTime &= 0x1ffff;
			/* 00. check if need to do sleep in this DTIM period.   If next beacon will arrive within 30ms , ...doesn't necessarily sleep. */
			/* TbTTTime uint = 64us, LEAD_TIME unit = 1024us, PsPollTime unit = 1ms */
			if (((64 * TbTTTime) < ((LEAD_TIME * 1024) + 40000))
			    && (TbttNumToNextWakeUp == 0)) {
				DBGPRINT(RT_DEBUG_TRACE,
					 ("TbTTTime = 0x%x , give up this sleep.\n", TbTTTime));
				OPSTATUS_CLEAR_FLAG(pAd, fOP_STATUS_DOZE);
				/*pAd->bPCIclkOffDisableTx = FALSE; */
				RTMP_CLEAR_PSFLAG(pAd, fRTMP_PS_DISABLE_TX);
				return FALSE;
			}
			PsPollTime = (64 * TbTTTime - LEAD_TIME * 1024) / 1000;
#if defined(RT3090) || defined(RT3592) || defined(RT3390) || defined(RT3593) || defined(RT5390) || defined(RT5392) || defined(RT5592)
			if (pAd->chipCap.HW_PCIE_PS_L3_ENABLE == TRUE) {
				PsPollTime -= 5;
			} else
#endif		/* defined(RT3090) || defined(RT3592) || defined(RT3390) || defined(RT3593) || defined(RT5390) || defined(RT5392) */
			PsPollTime -= 3;

			BeaconPeriodTime = pAd->CommonCfg.BeaconPeriod * 102 / 100;
			if (TbttNumToNextWakeUp > 0)
				PsPollTime += ((TbttNumToNextWakeUp - 1) * BeaconPeriodTime);

			pAd->Mlme.bPsPollTimerRunning = TRUE;
			RTMPSetTimer(&pAd->Mlme.PsPollTimer, PsPollTime);
		}
	} else {
		DBGPRINT(RT_DEBUG_TRACE, ("RT28xxPciAsicRadioOff::Level!=DOT11POWERSAVE\n"));
	}
#endif				/* PCIE_PS_SUPPORT */
#endif				/* CONFIG_STA_SUPPORT */
	/*pAd->bPCIclkOffDisableTx = FALSE; */
	RTMP_SET_FLAG(pAd, fRTMP_ADAPTER_IDLE_RADIO_OFF);

#ifdef CONFIG_STA_SUPPORT
	/* Set to 1R. */
	if (pAd->Antenna.field.RxPath > 1 && pAd->OpMode == OPMODE_STA) {
#ifdef RTMP_BBP
		if (pAd->chipCap.hif_type == HIF_RTMP) {
			UCHAR tempBBP_R3 = pAd->StaCfg.BBPR3 & 0xE7;

			RTMP_BBP_IO_WRITE8_BY_REG_ID(pAd, BBP_R3, tempBBP_R3);
		}
#endif				/* RTMP_BBP */
		/* TODO: shiang, how about RLT_BBP for this?? */
	}
#endif				/* CONFIG_STA_SUPPORT */

	/* In Radio Off, we turn off RF clk, So now need to call ASICSwitchChannel again. */
	if ((INFRA_ON(pAd) || pAd->OpMode == OPMODE_AP)
	    && (pAd->CommonCfg.CentralChannel != pAd->CommonCfg.Channel)
	    && (pAd->MlmeAux.HtCapability.HtCapInfo.ChannelWidth == BW_40))
		AsicTurnOffRFClk(pAd, pAd->CommonCfg.CentralChannel);
	else
		AsicTurnOffRFClk(pAd, pAd->CommonCfg.Channel);

	if (Level != RTMP_HALT) {
		UINT32 AutoWakeupInt = 0;
#ifdef RLT_MAC
		if (pAd->chipCap.hif_type == HIF_RLT)
			AutoWakeupInt = RLT_AutoWakeupInt;
#endif				/* RLT_MAC */
#ifdef RTMP_MAC
		if (pAd->chipCap.hif_type == HIF_RTMP)
			AutoWakeupInt = RTMP_AutoWakeupInt;
#endif				/* RTMP_MAC */
		/*
		 * Change Interrupt bitmask.
		 * When PCI clock is off, don't want to service interrupt.
		 */
		RTMP_IO_WRITE32(pAd, INT_MASK_CSR, AutoWakeupInt);
	} else {
		if (RTMP_TEST_FLAG(pAd, fRTMP_ADAPTER_INTERRUPT_ACTIVE))
			RTMP_ASIC_INTERRUPT_DISABLE(pAd);
	}

	RTMP_IO_WRITE32(pAd, pAd->RxRing[0].hw_cidx_addr, pAd->RxRing[0].RxCpuIdx);
	/*  2. Send Sleep command */
	RTMP_IO_WRITE32(pAd, H2M_MAILBOX_STATUS, 0xffffffff);
	RTMP_IO_WRITE32(pAd, H2M_MAILBOX_CID, 0xffffffff);
	/* send POWER-SAVE command to MCU. high-byte = 1 save power as much as possible. high byte = 0 save less power */
	AsicSendCommandToMcu(pAd, SLEEP_MCU_CMD, PowerSafeCID, 0xff, 0x1, FALSE);

#ifdef RT2860
	/* in rt3xxx, after issue SLEEP command, can't read/write register. So don't check Command ok. */
	if ((pAd->DeviceID == NIC2860_PCIe_DEVICE_ID)
	    || (pAd->DeviceID == NIC2790_PCIe_DEVICE_ID)) {
		/*  2-1. Wait command success */
		/* Status = 1 : success, Status = 2, already sleep, Status = 3, Maybe MAC is busy so can't finish this task. */
		brc = AsicCheckCommanOk(pAd, PowerSafeCID);

		/*  3. After 0x30 command is ok, send radio off command. lowbyte = 0 for power safe. */
		/* If 0x30 command is not ok this time, we can ignore 0x35 command. It will make sure not cause firmware'r problem. */
		if ((Level == DOT11POWERSAVE) && (brc == TRUE))
			AsicSendCmdToMcuAndWait(pAd, 0x35, PowerRadioOffCID, 0, 0x00, FALSE);	/* lowbyte = 0 means to do power safe, NOT turn off radio. */
		else if (brc == TRUE)
			AsicSendCmdToMcuAndWait(pAd, 0x35, PowerRadioOffCID, 1, 0x00, FALSE);	/* lowbyte = 0 means to do power safe, NOT turn off radio. */
	}
#endif				/* RT2860 */

#ifdef CONFIG_STA_SUPPORT
	/* 1. Wait DMA not busy */
	AsicWaitPDMAIdle(pAd, 50, 20);
#endif				/* CONFIG_STA_SUPPORT */

/* Disable for stability. If PCIE Link Control is modified for advance power save, re-covery this code segment.*/
/*RTMP_IO_WRITE32(pAd, PBF_SYS_CTRL, 0x1280);*/
/*OPSTATUS_SET_FLAG(pAd, fOP_STATUS_CLKSELECT_40MHZ);*/

#ifdef CONFIG_STA_SUPPORT
#ifdef PCIE_PS_SUPPORT
#if defined(RT3090) || defined(RT3592) || defined(RT3390) || defined(RT3593) || defined(RT5390) || defined(RT5392) || defined(RT5592)
/* Disable for stability. If PCIE Link Control is modified for advance power save, re-covery this code segment.*/
/*OPSTATUS_SET_FLAG(pAd, fOP_STATUS_CLKSELECT_40MHZ);*/
	if (pAd->chipCap.HW_PCIE_PS_L3_ENABLE == TRUE) {
		DBGPRINT(RT_DEBUG_TRACE,
			 ("RT28xxPciAsicRadioOff::3090 return to skip the following TbttNumToNextWakeUp setting for 279x\n"));
		pAd->bPCIclkOff = TRUE;
		RTMP_CLEAR_PSFLAG(pAd, fRTMP_PS_DISABLE_TX);
		/* For this case, doesn't need to below actions, so return here. */
		/*return brc; */
		return TRUE;
	}
#endif				/* #if defined(RT3090) || defined(RT3592) || defined(RT3390) || defined(RT3593) || defined(RT5390) || defined(RT5392) */
#endif				/* PCIE_PS_SUPPORT */
#endif				/* CONFIG_STA_SUPPORT */
	if (Level == DOT11POWERSAVE) {
		AUTO_WAKEUP_STRUC AutoWakeupCfg;
		/*RTMPSetTimer(&pAd->Mlme.PsPollTimer, 90); */

		/* we have decided to SLEEP, so at least do it for a BEACON period. */
		if (TbttNumToNextWakeUp == 0)
			TbttNumToNextWakeUp = 1;

		AutoWakeupCfg.word = 0;
		RTMP_IO_WRITE32(pAd, AUTO_WAKEUP_CFG, AutoWakeupCfg.word);

		/* 1. Set auto wake up timer. */
		AutoWakeupCfg.field.NumofSleepingTbtt = TbttNumToNextWakeUp - 1;
		AutoWakeupCfg.field.EnableAutoWakeup = 1;
		AutoWakeupCfg.field.AutoLeadTime = LEAD_TIME;
		RTMP_IO_WRITE32(pAd, AUTO_WAKEUP_CFG, AutoWakeupCfg.word);
	}
#ifdef CONFIG_STA_SUPPORT
#ifdef PCIE_PS_SUPPORT

	/*  4-1. If it's to disable our device. Need to restore PCI Configuration Space to its original value. */
	if (Level == RTMP_HALT && pAd->OpMode == OPMODE_STA) {
		if ((brc == TRUE) && (i < 50))
			RTMPPCIeLinkCtrlSetting(pAd, 1);
	}
	/*  4. Set PCI configuration Space Link Comtrol fields.  Only Radio Off needs to call this function */
	else if (pAd->OpMode == OPMODE_STA) {
		if ((brc == TRUE) && (i < 50))
			RTMPPCIeLinkCtrlSetting(pAd, 3);
	}
	RTMP_CLEAR_PSFLAG(pAd, fRTMP_PS_DISABLE_TX);
#endif				/* PCIE_PS_SUPPORT */
#endif				/* CONFIG_STA_SUPPORT */

	/*pAd->bPCIclkOffDisableTx = FALSE; */
#ifdef RT3290
	if (IS_RT3290(pAd)) {
		AsicCheckCommanOk(pAd, PowerSafeCID);
		RTMPEnableWlan(pAd, FALSE, FALSE);
	}
#endif				/* RT3290 */

	return TRUE;
}


VOID RT28xxPciMlmeRadioOn(RTMP_ADAPTER *pAd)
{
	if (!RTMP_TEST_FLAG(pAd, fRTMP_ADAPTER_RADIO_OFF))
		return;

	DBGPRINT(RT_DEBUG_TRACE, ("%s===>\n", __func__));

#if defined(MT76x0) || defined(MT76x2)
	if (IS_MT76x0(pAd) || IS_MT76x2(pAd)) {
		MT76xx_PciMlmeRadioOn(pAd);
		return;
	}
#endif				/* defined(MT76x0) || defined(MT76x2) */

	if ((pAd->OpMode == OPMODE_AP) || ((pAd->OpMode == OPMODE_STA)
#ifdef CONFIG_STA_SUPPORT
#ifdef PCIE_PS_SUPPORT
					   &&
					   (!OPSTATUS_TEST_FLAG
					    (pAd, fOP_STATUS_ADVANCE_POWER_SAVE_PCIE_DEVICE)
					    || pAd->StaCfg.PSControl.field.EnableNewPS == FALSE)
#endif				/* PCIE_PS_SUPPORT */
#endif				/* CONFIG_STA_SUPPORT */
	    )) {
		RTMPRingCleanUp(pAd, QID_AC_BK);
		RTMPRingCleanUp(pAd, QID_AC_BE);
		RTMPRingCleanUp(pAd, QID_AC_VI);
		RTMPRingCleanUp(pAd, QID_AC_VO);
		RTMPRingCleanUp(pAd, QID_HCCA);
		RTMPRingCleanUp(pAd, QID_MGMT);
		RTMPRingCleanUp(pAd, QID_RX);

#ifdef RTMP_RBUS_SUPPORT
		if (pAd->infType == RTMP_DEV_INF_RBUS)
			NICResetFromError(pAd);
#endif				/* RTMP_RBUS_SUPPORT */
#ifdef RTMP_PCI_SUPPORT
#ifdef RT65xx
		if (IS_RT8592(pAd)) {
#ifdef RT8592
			if (IS_RT8592(pAd))
				RTMP_ASIC_INTERRUPT_ENABLE(pAd);
#endif				/* RT8592 */
#ifdef DOT11_VHT_AC
			if (pAd->CommonCfg.BBPCurrentBW == BW_80)
				pAd->hw_cfg.cent_ch = pAd->CommonCfg.vht_cent_ch;
			else
#endif				/* DOT11_VHT_AC */
				pAd->hw_cfg.cent_ch = pAd->CommonCfg.CentralChannel;
			AsicSwitchChannel(pAd, pAd->hw_cfg.cent_ch, FALSE);
			AsicLockChannel(pAd, pAd->hw_cfg.cent_ch);
		} else
#endif				/* RT65xx */
		{
			if (pAd->infType == RTMP_DEV_INF_PCI || pAd->infType == RTMP_DEV_INF_PCIE)
				RT28xxPciAsicRadioOn(pAd, GUI_IDLE_POWER_SAVE);
		}
#endif				/* RTMP_PCI_SUPPORT */

		/* Enable Tx/Rx */
		RTMPEnableRxTx(pAd);

		/* Clear Radio off flag */
		RTMP_CLEAR_FLAG(pAd, fRTMP_ADAPTER_RADIO_OFF);
		RTMP_CLEAR_FLAG(pAd, fRTMP_ADAPTER_IDLE_RADIO_OFF);

#ifdef LED_CONTROL_SUPPORT
		/* Set LED */
		RTMPSetLED(pAd, LED_RADIO_ON);
#ifdef CONFIG_AP_SUPPORT
		/* The LEN_RADIO_ON indicates "Radio on but link down",
		 * so AP shall set LED LINK_UP status
		 */
		IF_DEV_CONFIG_OPMODE_ON_AP(pAd)
			RTMPSetLED(pAd, LED_LINK_UP);
#endif				/* CONFIG_AP_SUPPORT */
#endif				/* LED_CONTROL_SUPPORT */
	}
#ifdef CONFIG_STA_SUPPORT
#ifdef PCIE_PS_SUPPORT

	if ((pAd->OpMode == OPMODE_STA) &&
	    (OPSTATUS_TEST_FLAG(pAd, fOP_STATUS_ADVANCE_POWER_SAVE_PCIE_DEVICE))
	    && (pAd->StaCfg.PSControl.field.EnableNewPS == TRUE)) {
		BOOLEAN Cancelled;

		RTMPPCIeLinkCtrlValueRestore(pAd, RESTORE_WAKEUP);

		pAd->Mlme.bPsPollTimerRunning = FALSE;
		RTMPCancelTimer(&pAd->Mlme.PsPollTimer, &Cancelled);
		RTMPCancelTimer(&pAd->Mlme.RadioOnOffTimer, &Cancelled);
		RTMPSetTimer(&pAd->Mlme.RadioOnOffTimer, 40);
	}
#endif				/* PCIE_PS_SUPPORT */
#endif				/* CONFIG_STA_SUPPORT */
}


VOID RT28xxPciMlmeRadioOFF(RTMP_ADAPTER *pAd)
{
	if (RTMP_TEST_FLAG(pAd, fRTMP_ADAPTER_RADIO_OFF))
		return;

	DBGPRINT(RT_DEBUG_TRACE, ("%s===>\n", __func__));

#if defined(MT76x0) || defined(MT76x2)
	if (IS_MT76x0(pAd) || IS_MT76x2(pAd)) {
		MT76xx_PciMlmeRadioOFF(pAd);
		return;
	}
#endif				/* MT76x0 */

	/* Set Radio off flag */
	RTMP_SET_FLAG(pAd, fRTMP_ADAPTER_RADIO_OFF);
#ifdef BB_SOC
	RTMP_SET_FLAG(pAd, fRTMP_ADAPTER_POLL_IDLE);
#endif				/* BB_SOC */

#ifdef CONFIG_STA_SUPPORT
	IF_DEV_CONFIG_OPMODE_ON_STA(pAd) {
		BOOLEAN Cancelled;

		if (RTMP_TEST_FLAG(pAd, fRTMP_ADAPTER_BSS_SCAN_IN_PROGRESS)) {
			RTMPCancelTimer(&pAd->MlmeAux.ScanTimer, &Cancelled);
			RTMP_CLEAR_FLAG(pAd, fRTMP_ADAPTER_BSS_SCAN_IN_PROGRESS);
		}
#ifdef RTMP_PCI_SUPPORT
#ifdef PCIE_PS_SUPPORT
		/* If during power safe mode. */
		if ((pAd->StaCfg.bRadio == TRUE)
		    && (OPSTATUS_TEST_FLAG(pAd, fOP_STATUS_ADVANCE_POWER_SAVE_PCIE_DEVICE)
			&& pAd->StaCfg.PSControl.field.EnableNewPS == TRUE)) {
			DBGPRINT(RT_DEBUG_TRACE,
				 ("-->MlmeRadioOff() return on bRadio == TRUE;\n"));
			return;
		}
#endif				/* PCIE_PS_SUPPORT */

		/* Always radio on since the NIC needs to set the MCU command (LED_RADIO_OFF). */
		if (pAd->infType == RTMP_DEV_INF_PCI || pAd->infType == RTMP_DEV_INF_PCIE)
			if (IDLE_ON(pAd) && RTMP_TEST_FLAG(pAd, fRTMP_ADAPTER_IDLE_RADIO_OFF))
				RT28xxPciAsicRadioOn(pAd, GUI_IDLE_POWER_SAVE);
#ifdef PCIE_PS_SUPPORT
		if (OPSTATUS_TEST_FLAG(pAd, fOP_STATUS_ADVANCE_POWER_SAVE_PCIE_DEVICE)
		    && pAd->StaCfg.PSControl.field.EnableNewPS == TRUE) {
			BOOLEAN Cancelled;

			pAd->Mlme.bPsPollTimerRunning = FALSE;
			RTMPCancelTimer(&pAd->Mlme.PsPollTimer, &Cancelled);
			RTMPCancelTimer(&pAd->Mlme.RadioOnOffTimer, &Cancelled);
		}
#endif				/* PCIE_PS_SUPPORT */
#endif				/* RTMP_PCI_SUPPORT */

		/* Link down first if any association exists    */
		if (INFRA_ON(pAd) || ADHOC_ON(pAd))
			LinkDown(pAd, FALSE);

		RtmpusecDelay(10000);

	/*==========================================    */
		/* Clean up old bss table   */
		BssTableInit(&pAd->ScanTab);
	}
#endif				/* CONFIG_STA_SUPPORT */

#ifdef CONFIG_AP_SUPPORT
#ifdef AP_SCAN_SUPPORT
	{
		BOOLEAN Cancelled;

		RTMPCancelTimer(&pAd->MlmeAux.APScanTimer, &Cancelled);
	}
#endif				/* AP_SCAN_SUPPORT */
#endif				/* CONFIG_AP_SUPPORT */

#ifdef LED_CONTROL_SUPPORT
	RTMPSetLED(pAd, LED_RADIO_OFF);
#endif				/* LED_CONTROL_SUPPORT */

#ifdef RTMP_PCI_SUPPORT
#ifdef CONFIG_STA_SUPPORT
#ifdef PCIE_PS_SUPPORT
/*Q:Does all PCIe devices need to use timer to execute radio off function? or only if the device is PCIe and EnableNewPS is true ?*/
/*A:It is right, because only when the PCIe and EnableNewPs is true, we need to delay the RadioOffTimer*/
/*to avoid the deadlock with PCIe Power saving function. */
	if (pAd->OpMode == OPMODE_STA &&
	    OPSTATUS_TEST_FLAG(pAd, fOP_STATUS_ADVANCE_POWER_SAVE_PCIE_DEVICE) &&
	    pAd->StaCfg.PSControl.field.EnableNewPS == TRUE) {
		RTMPSetTimer(&pAd->Mlme.RadioOnOffTimer, 10);
	} else
#endif				/* PCIE_PS_SUPPORT */
#endif				/* CONFIG_STA_SUPPORT */
	if (pAd->infType == RTMP_DEV_INF_PCI || pAd->infType == RTMP_DEV_INF_PCIE) {
#ifdef RT8592
		if (IS_RT8592(pAd)) {
			DISABLE_TX_RX(pAd, GUIRADIO_OFF);
			if (RTMP_TEST_FLAG(pAd, fRTMP_ADAPTER_INTERRUPT_ACTIVE))
				RTMP_ASIC_INTERRUPT_DISABLE(pAd);
		} else
#endif				/* RT8592 */
		{
			if (RT28xxPciAsicRadioOff(pAd, GUIRADIO_OFF, 0) == FALSE) {
				DBGPRINT(RT_DEBUG_ERROR,
					 ("%s call RT28xxPciAsicRadioOff fail !!\n", __func__));
			}
		}
	}
#endif				/* RTMP_PCI_SUPPORT */

#ifdef RTMP_RBUS_SUPPORT
	if (pAd->infType == RTMP_DEV_INF_RBUS) {
		int i;
		WPDMA_GLO_CFG_STRUC GloCfg;

		/* Disable Tx/Rx DMA */
		RTMP_IO_READ32(pAd, WPDMA_GLO_CFG, &GloCfg.word);	/* disable DMA */
		GloCfg.field.EnableTxDMA = 0;
		GloCfg.field.EnableRxDMA = 0;
		RTMP_IO_WRITE32(pAd, WPDMA_GLO_CFG, GloCfg.word);	/* abort all TX rings */


		/* MAC_SYS_CTRL => value = 0x0 => 40mA */
		RTMP_IO_WRITE32(pAd, MAC_SYS_CTRL, 0);

		/* PWR_PIN_CFG => value = 0x0 => 40mA */
		RTMP_IO_WRITE32(pAd, PWR_PIN_CFG, 0);

		/* TX_PIN_CFG => value = 0x0 => 20mA */
		RTMP_IO_WRITE32(pAd, TX_PIN_CFG, 0x00010000);

		/* TODO: shiang-6590, here we need to make sure the CentralChannel is the same as Channel. */
		if (pAd->CommonCfg.CentralChannel)
			AsicTurnOffRFClk(pAd, pAd->CommonCfg.CentralChannel);
		else
			AsicTurnOffRFClk(pAd, pAd->CommonCfg.Channel);

		/* Waiting for DMA idle */
		AsicWaitPDMAIdle(pAd, 100, 1000);
	}
#endif				/* RTMP_RBUS_SUPPORT */
}


/*
 *========================================================================
 *Routine Description:
 *	Get a pci map buffer.
 *
 *Arguments:
 *	pAd				- WLAN control block pointer
 *	*ptr			- Virtual address or TX control block
 *	size			- buffer size
 *	sd_idx			- 1: the ptr is TX control block
 *	direction		- RTMP_PCI_DMA_TODEVICE or RTMP_PCI_DMA_FROMDEVICE
 *
 *Return Value:
 *	the PCI map buffer
 *
 *Note:
 *========================================================================
 */
ra_dma_addr_t RtmpDrvPciMapSingle(RTMP_ADAPTER *pAd, VOID *ptr, size_t size,
				  INT sd_idx, INT direction)
{
	struct pci_dev *pci_dev;

	if (pAd->infType == RTMP_DEV_INF_RBUS)
		pci_dev = NULL;
	else
		pci_dev = pAd->OS_Cookie->pci_dev;

	if (sd_idx == 1) {
		TX_BLK *pTxBlk = (TX_BLK *) ptr;

		if (!pTxBlk->SrcBufLen)
			return (ra_dma_addr_t) 0;

		return linux_pci_map_single(pci_dev, pTxBlk->pSrcBufData,
					    pTxBlk->SrcBufLen, 0, direction);
	}

	return linux_pci_map_single(pci_dev, ptr, size, 0, direction);
}


int write_reg(RTMP_ADAPTER *ad, UINT32 base, UINT16 offset, UINT32 value)
{
	if (base == 0x40)
		RTMP_IO_WRITE32(ad, 0x10000 + offset, value);
	else if (base == 0x41)
		RTMP_IO_WRITE32(ad, offset, value);
	else
		DBGPRINT(RT_DEBUG_OFF, ("illegal base = %x\n", base));

	return 0;
}


int read_reg(RTMP_ADAPTER *ad, UINT32 base, UINT16 offset, UINT32 *value)
{
	if (base == 0x40)
		RTMP_IO_READ32(ad, 0x10000 + offset, value);
	else if (base == 0x41)
		RTMP_IO_READ32(ad, offset, value);
	else
		DBGPRINT(RT_DEBUG_OFF, ("illegal base = %x\n", base));
	return 0;
}

INT rtmp_irq_init(RTMP_ADAPTER *pAd)
{
	unsigned long _irqFlags;
	UINT32 reg_mask = 0;

#ifdef RLT_MAC
	if (pAd->chipCap.hif_type == HIF_RLT)
		reg_mask = (RLT_DELAYINTMASK) | (RLT_RxINT | RLT_TxDataInt | RLT_TxMgmtInt);
#endif				/* RLT_MAC */

#ifdef RTMP_MAC
	if (pAd->chipCap.hif_type == HIF_RTMP)
		reg_mask =
		    ((RTMP_DELAYINTMASK) | (RTMP_RxINT | RTMP_TxDataInt | RTMP_TxMgmtInt)) &
		    ~(0x03);
#endif				/* RTMP_MAC */

	RTMP_INT_LOCK(&pAd->irq_lock, _irqFlags);
	pAd->int_enable_reg = reg_mask;
	pAd->int_disable_mask = 0;
	pAd->int_pending = 0;
	RTMP_INT_UNLOCK(&pAd->irq_lock, _irqFlags);

	return 0;
}

#endif				/* RTMP_MAC_PCI */
